在向洛谷的API发起POST请求的时候,需要在请求头中携带一个X-CSRF-Token请求头。
 获取CSRF Token
我们打开洛谷,按F12,在元素项可以找到这个:

这个就是我们所需要的CSRF Token。
 提取CSRF Token
通过正则表达式,我们很轻松就能写出代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | const axios = require("axios").defaultconst 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()作用为显示验证码,并反馈用户输入的验证码。
| 12
 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请求头:
| 12
 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。
| 12
 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)
 
 | 
这样,我们就可以成功实现登录的逻辑了。