在向洛谷的API发起POST
请求的时候,需要在请求头中携带一个X-CSRF-Token
请求头。
获取CSRF Token
我们打开洛谷,按F12
,在元素项可以找到这个:
这个就是我们所需要的CSRF Token。
提取CSRF Token
通过正则表达式,我们很轻松就能写出代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const axios = require("axios").default const cookieSupporter = require("axios-cookiejar-support").default const cookieJar = require('tough-cookie') let jar = new cookieJar.CookieJar() let _ = cookieSupporter(axios.create({ baseURL: "https://www.luogu.com.cn", withCredentials: true, jar: jar })) ....... async function getToken() { let token=await _.get('/').then(html=>{ var Token_REG=new RegExp(/<meta name="csrf-token" content="(.*)">/); var execData=Token_REG.exec(html.data) console.log(execData) return execData ? execData[1].trim():null }) return token; }
|
getToken()
函数会获取https://www.luogu.com.cm/
的网页(返回HTML
),随后通过正则表达式提取出CSRF Token。
实装
我们模拟一个登陆的流程,假定showCaptcha()
作用为显示验证码,并反馈用户输入的验证码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| let username = "pai233" let password = "Pa55w0rd" let captcha = await showCaptcha() let loginStatus=await _.post('/api/auth/userPassLogin',{ username, password, captcha }, { headers: { 'X-CSRF-TOKEN': await getToken(), 'Referer': 'https://www.luogu.com.cn/auth/login', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'Origin': 'https://www.luogu.com.cn/', 'X-Requested-With': 'XMLHttpRequest' } }).catch(err=>{ if(err.response){ return err.response.data } }) console.log(loginStatus.status)
|
这时会发现API返回了400 Bad Request
。原因是我们的CSRF Token不是从请求头中的Referer
中获取的。
Tips:部分时间测试时发现会直接返回200 OK
并登陆成功,但为防止洛谷再次修改API,建议阅读下面流程!
修改
方案一:修改Referer
因为我们需要从Referer中获取CSRF Token,所以我们可以直接修改Referer
请求头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let loginStatus=await _.post('/api/auth/userPassLogin',{ username, password, captcha }, { headers: { 'X-CSRF-TOKEN': await getToken(), 'Referer': 'https://www.luogu.com.cn/', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'Origin': 'https://www.luogu.com.cn/', 'X-Requested-With': 'XMLHttpRequest' } }).catch(err=>{ if(err.response){ return err.response.data } }) console.log(loginStatus.status)
|
但这样会很奇怪,我明明是要登陆的,但在首页怎么能登陆呢?都没有入口…… (账号:危)
方案二:修改getToken()
的请求位置
我们可以通过传参的方式,让getToken()
返回Referer
中的CSRF Token。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| async function getToken(referer) { let token=await _.get(referer).then(html=>{ var Token_REG=new RegExp(/<meta name="csrf-token" content="(.*)">/); var execData=Token_REG.exec(html.data) console.log(execData) return execData ? execData[1].trim():null }) return token; } ....... let loginStatus=await _.post('/api/auth/userPassLogin',{ username, password, captcha }, { headers: { 'X-CSRF-TOKEN': await getToken('/auth/login'), 'Referer': 'https://www.luogu.com.cn/auth/login', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'Origin': 'https://www.luogu.com.cn/', 'X-Requested-With': 'XMLHttpRequest' } }).catch(err=>{ if(err.response){ return err.response.data } }) console.log(loginStatus.status)
|
这样,我们就可以成功实现登录的逻辑了。