Vue表单提交时如何同时实现CSRF防护和验证码验证?

极客筱萌 阅读 45

在开发登录表单时,我需要同时处理CSRF防护和验证码验证。现在遇到的问题是:当用户刷新验证码时,CSRF令牌没有同步更新,导致表单提交时后端返回验证失败。我尝试在获取验证码接口里携带CSRF令牌,但发现令牌值始终是初始化时的旧值。

这是我的Vue表单组件代码:


<template>
  <form @submit.prevent="submitForm">
    <input v-model="formData.username">
    <input v-model="formData.password">
    <img :src="captchaUrl" @click="refreshCaptcha">
    <input v-model="formData.code">
    <button type="submit">登录</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      formData: {
        username: '',
        password: '',
        code: '',
        _csrf: '' // 从cookie获取的初始值
      },
      captchaUrl: <code>/api/captcha?_=${Date.now()}</code>
    }
  },
  methods: {
    refreshCaptcha() {
      this.captchaUrl = <code>/api/captcha?_=${Date.now()}</code>;
    },
    async submitForm() {
      try {
        await this.$axios.post('/login', this.formData, {
          headers: {'X-CSRF-Token': this.formData._csrf}
        });
      } catch(err) {
        console.error('验证失败', err);
      }
    }
  }
}
</script>

问题具体出现在:当我点击刷新验证码时,虽然图片更新了,但后端返回的新的CSRF令牌没有被正确捕获。我应该在哪个生命周期阶段重新获取CSRF令牌?是否需要在刷新验证码时同时更新令牌?

我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
UE丶司卿
这个坑我之前也踩过,说白了就是CSRF令牌没有同步更新。

你的问题在于formData._csrf只在组件初始化时从cookie拿了一次,之后就固定了。但后端在某些操作(比如刷新验证码)时会刷新CSRF令牌,你没拿到新的。

解决思路是:在刷新验证码的接口响应中获取后端返回的新令牌。

后端需要配合,在验证码接口的响应头里带上新的CSRF令牌,比如:

refreshCaptcha() {
// 每次刷新都发请求,从响应头拿新令牌 this.$axios.get('/api/captcha', {
params: { _: Date.now() }
}).then(res => {
// 从响应头取新令牌,更新本地
const newToken = res.headers['x-csrf-token'];
if (newToken) {
this.formData._csrf = newToken;
}
// 更新验证码图片
this.captchaUrl = /api/captcha?_=${Date.now()};
});
}


后端那边要在刷新验证码的接口里,在响应头里带上新的令牌值。你用的什么后端语言?PHP的话可以这样:

header('X-CSRF-Token: ' . csrf_token());
// 或者直接设置cookie让前端同步
setcookie('XSRF-TOKEN', csrf_token(), 0, '/');


还有个取巧的办法是让后端每次都在验证码接口的响应里返回新令牌,前端直接用就行,这样就不用来回折腾响应头了。

总之核心就是:别偷懒,刷新验证码时必须同步更新CSRF令牌。
点赞
2026-03-19 12:11
Air-俊蓓
问题出在CSRF令牌没有实时更新。我的做法是每次刷新验证码时主动从后端获取新令牌,同时更新表单里的_csrf字段。

具体改动如下:

1. 获取验证码的接口应该返回两个值:新的CSRF令牌 + 新的验证码图片地址。比如返回结构是:
{
csrfToken: 'abc123',
captchaUrl: 'data:image/png;base64,...'
}


2. 修改refreshCaptcha方法,用axios请求获取新验证码和新令牌:
async refreshCaptcha() {
const res = await this.$axios.get('/api/captcha');
this.formData._csrf = res.data.csrfToken;
this.captchaUrl = res.data.captchaUrl;
}


3. 初始化的时候也要调用一次refreshCaptcha,这样表单里的_csrf就不会是空值

4. 提交表单的时候只需要传formData,不需要额外带headers。因为后端验证CSRF时会从表单字段里取这个令牌。

这样改完就能保证每次刷新验证码时,CSRF令牌也是最新的。后端需要配合把csrfToken写进响应里。
点赞 9
2026-02-07 16:01