在向洛谷的API发起POST请求的时候,需要在请求头中携带一个X-CSRF-Token请求头。

获取CSRF Token

我们打开洛谷,按F12,在元素项可以找到这个:
image.png

这个就是我们所需要的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)

这样,我们就可以成功实现登录的逻辑了。