You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express.js csurf报invalid csrf token错误及跨Django/Express适配问题

解决Express.js csurf「invalid csrf token」错误及跨Django/Express前端适配问题

一、Express csrf token无效问题排查与解决

1. 检查中间件顺序(核心!)

csurf 依赖 cookie 或 session 解析结果,必须放在cookie-parser、body 解析中间件之后,路由之前。错误顺序会直接导致 token 无法读取:

const express = require('express');
const cookieParser = require('cookie-parser');
const csurf = require('csurf');
const app = express();

// 正确顺序:先解析cookie
app.use(cookieParser());
// 解析表单数据(表单提交必加)
app.use(express.urlencoded({ extended: true }));
// 解析JSON(AJAX JSON提交必加)
app.use(express.json());

// 配置csurf,用cookie存储token(适合无session场景)
const csrfProtection = csurf({
  cookie: {
    httpOnly: false, // 允许前端JS读取cookie获取token(若用模板注入可设为true)
    secure: process.env.NODE_ENV === 'production', // 生产环境HTTPS下开启
    sameSite: process.env.NODE_ENV === 'production' ? 'Strict' : 'Lax',
    path: '/' // 确保所有路由都能访问到cookie
  }
});

// 给需要校验的路由组应用中间件(比如/admin下的所有路由)
app.use('/admin', csrfProtection);

2. 确保token传递方式正确

根据请求类型,前端需对应传递token:

  • 表单提交:添加隐藏字段_csrf(字段名需与csurf配置一致,默认是_csrf)
    <form method="POST" action="/admin/upload">
      <input type="hidden" name="_csrf" value="<%= csrfToken %>">
      <!-- 其他表单字段 -->
    </form>
    
  • AJAX请求:将token放入X-CSRF-Token请求头(或配置中指定的头名)
    // 从cookie提取token的工具函数
    function getCsrfToken() {
      const match = document.cookie.match(/(^| )_csrf=([^;]+)/);
      return match ? match[2] : '';
    }
    
    // 上传GIF的AJAX示例
    async function uploadGif(formData) {
      const res = await fetch('/admin/upload', {
        method: 'POST',
        headers: {
          'X-CSRF-Token': getCsrfToken()
        },
        body: formData,
        credentials: 'include' // 必须携带cookie
      });
    }
    

3. 跨域场景额外配置

如果前端与Express不在同一域名,需:

  • 修改csurf的cookie配置:sameSite: 'None', secure: true
  • 前端请求添加credentials: 'include'
  • Express配置CORS允许携带凭证:
    const cors = require('cors');
    app.use(cors({
      origin: 'https://your-frontend-domain.com',
      credentials: true
    }));
    

二、同一前端适配Django/Express方案

通过后端注入环境标识,前端动态适配CSRF规则与API地址:

1. 后端注入环境元信息

  • Django模板:
    <meta name="backend-env" content="django">
    <meta name="csrf-token" content="{{ csrf_token }}">
    
  • Express模板:
    <meta name="backend-env" content="express">
    <meta name="csrf-token" content="<%= csrfToken %>">
    

2. 前端统一请求逻辑

封装通用请求函数,根据环境自动切换CSRF头与API前缀:

// 初始化环境配置
const backendEnv = document.querySelector('meta[name="backend-env"]').content;
const csrfTokenMeta = document.querySelector('meta[name="csrf-token"]');
let csrfToken = csrfTokenMeta ? csrfTokenMeta.content : '';
const apiBaseUrl = backendEnv === 'django' ? '/django-api' : '/express-api';

// 通用请求函数
async function sendRequest(url, options = {}) {
  const headers = { ...options.headers };
  const method = (options.method || 'GET').toUpperCase();

  // 非安全请求添加CSRF token
  if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) {
    if (backendEnv === 'django') {
      headers['X-CSRFToken'] = csrfToken; // Django专用头
    } else {
      headers['X-CSRF-Token'] = csrfToken || getCsrfToken(); // Express兼容两种获取方式
    }
  }

  return fetch(`${apiBaseUrl}${url}`, {
    ...options,
    headers,
    credentials: 'include' // 携带cookie
  });
}

// 工具函数:从cookie取Express的CSRF token
function getCsrfToken() {
  const match = document.cookie.match(/(^| )_csrf=([^;]+)/);
  return match ? match[2] : '';
}

3. 动态适配表单属性

如果用表单提交,动态设置action和CSRF字段:

<form id="upload-form" method="POST" enctype="multipart/form-data">
  <!-- 表单字段 -->
</form>

<script>
const form = document.getElementById('upload-form');
form.action = `${apiBaseUrl}/upload`;

// Express环境添加隐藏CSRF字段(若不用请求头)
if (backendEnv === 'express') {
  const csrfInput = document.createElement('input');
  csrfInput.type = 'hidden';
  csrfInput.name = '_csrf';
  csrfInput.value = csrfToken || getCsrfToken();
  form.appendChild(csrfInput);
}
</script>

内容的提问来源于stack exchange,提问作者Maxym Matskiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:07:32