Vue项目CSRF防护时验证请求头总失败怎么办?
我在Vue项目里用axios发请求时设置了X-CSRF-Token头,但后端一直返回403错误。之前在登录接口成功获取到token并存到cookie里了,代码是这样的:
<script>
import axios from 'axios';
axios.interceptors.request.use(config => {
config.headers['X-CSRF-Token'] = document.cookie.match(/csrfToken=([w-]+)/)[1];
return config;
});
</script>
后端同学说没有收到这个请求头,但我在浏览器Network面板里明明能看到请求头有X-CSRF-Token字段。难道是Vue的axios配置哪里有问题?或者需要配合其他设置?
首先说原理是这样:当你在请求里加了自定义头比如X-CSRF-Token,浏览器会认为这是一个“非简单请求”,就会先发一个OPTIONS请求问服务器,“我能不能发这个带token的请求”。这个叫预检请求。如果后端没正确响应这个OPTIONS请求,或者没声明允许X-CSRF-Token这个头,那浏览器就不会继续发真正的POST/GET请求,即使你在Network里看到了头,其实那个请求根本没发出去,或者是被拦截了。
所以第一步,你要确认后端有没有处理OPTIONS请求,并且返回正确的CORS头。需要至少包括:
Access-Control-Allow-Origin: 你的前端域名
Access-Control-Allow-Credentials: true (如果你带cookie)
Access-Control-Allow-Headers: X-CSRF-Token, Content-Type (这里必须包含你用的头)
特别是Access-Control-Allow-Headers,如果这里没写X-CSRF-Token,浏览器就会拒绝发送你的实际请求。
然后第二步,你的前端代码也有点小问题。你这句:
document.cookie.match(/csrfToken=([w-]+)/)[1]正则写错了。应该是
w而不是w,少了个反斜杠。而且没做空值判断,万一没取到cookie就崩了。建议改成这样:这样更健壮,不会因为cookie不存在就报错。
第三步,确保你发请求的时候带了凭据。比如用了withCredentials:
或者全局设置:
因为CSRF token通常要配合cookie一起验证,不带凭据的话,后端没法关联你的session和token。
最后提醒一下,有些后端框架比如Spring Security、Django、Laravel这些,对CSRF的header名有默认要求。比如有的默认认X-XSRF-TOKEN,而不是X-CSRF-Token。你得跟后端同学确认他们那边配置的是哪个header名。如果是框架自带的机制,别强行用自定义名字,容易出问题。
总结一下解决步骤:
1. 改正前端代码里的cookie读取逻辑,避免正则错误和空值崩溃
2. 确保axios发请求时带了withCredentials: true
3. 让后端处理OPTIONS预检请求,正确返回Access-Control-Allow-Headers包含你的token头
4. 和后端核对CSRF token的header名称是否一致
做完这些,99%的问题都能解决。要是还有问题,打开浏览器的Network面板,找那个OPTIONS请求,看它的响应头有没有Access-Control-Allow-Headers,有没有你的X-CSRF-Token。如果没有,就是后端没配好,不用往下查了,前端再怎么改都没用。
你虽然在axios里加了X-CSRF-Token头,但这个属于自定义头部,浏览器会在发正式请求前先发一个OPTIONS请求做预检。如果后端没正确响应这个OPTIONS请求,允许你的自定义头,那实际请求就不会发出去,自然收不到。
解决办法有两个:
第一个是让后端在CORS配置里加上对X-CSRF-Token头的支持,比如在响应头里加上:
Access-Control-Allow-Headers: X-CSRF-Token而且要确保OPTIONS请求返回200,不能403/404。
第二个是别用自定义头,把token塞到cookie里,后端从cookie读。这样不触发预检,简单粗暴。你都已经存到cookie了,其实可以让后端直接验证cookie里的token,不用再手动设请求头。
另外你那段取cookie的代码也有点问题,
[w-]+应该是w-吧?少了个反斜杠。建议改成:document.cookie.match(/csrfToken=([^;]+)/)这样更稳妥。
优先让后端同学看看服务日志,确认到底有没有收到OPTIONS预检请求,就知道是不是CORS的问题了。