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

如何在React.js与Express.js API中持久化AccessToken?

React + Express 实现安全的登录会话持久化方案

这是比LocalStorage更安全的方案,HttpOnly Cookie无法被前端JS读取,从根源避免XSS窃取会话的风险,配合CSRF防护就能解决你的问题。

后端(Express)配置步骤

  1. 安装依赖:
npm install express-session cookie-parser
  1. 配置session和cookie:
const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const app = express();

app.use(cookieParser());
app.use(session({
  secret: '你的高强度随机密钥', // 建议用环境变量存储,不要硬编码
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true, // 核心设置:禁止前端JS访问Cookie
    secure: process.env.NODE_ENV === 'production', // 生产环境必须开启HTTPS才生效
    sameSite: 'strict', // 防范CSRF的关键设置之一
    maxAge: 24 * 60 * 60 * 1000 // 会话有效期设为1天
  }
}));

// 登录接口示例
app.post('/api/login', (req, res) => {
  // 此处替换为实际的账号密码验证逻辑
  const user = { id: 1, username: 'test_user' };
  req.session.user = user; // 将用户信息存入服务器Session
  res.status(200).json({ message: '登录成功' });
});

// 验证会话状态的接口示例
app.get('/api/profile', (req, res) => {
  if (req.session.user) {
    res.json({ user: req.session.user });
  } else {
    res.status(401).json({ message: '未登录' });
  }
});

// 登出接口
app.post('/api/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) {
      return res.status(500).json({ message: '登出失败' });
    }
    res.clearCookie('connect.sid'); // 清除对应的Session Cookie
    res.status(200).json({ message: '登出成功' });
  });
});

前端(React)处理

浏览器会自动携带HttpOnly Cookie,前端无需手动存储会话信息,只需确保请求携带凭证:

  1. 登录请求示例:
const handleLogin = async () => {
  try {
    const response = await fetch('/api/login', {
      method: 'POST',
      credentials: 'include', // 关键:告诉浏览器携带Cookie
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ username: 'test_user', password: '123456' })
    });
    const data = await response.json();
    // 登录成功后更新页面状态或跳转
  } catch (err) {
    console.error('登录失败:', err);
  }
};
  1. 获取用户信息(验证会话):
const fetchUserProfile = async () => {
  try {
    const response = await fetch('/api/profile', {
      credentials: 'include'
    });
    if (response.status === 401) {
      // 未登录状态,跳转至登录页
      window.location.href = '/login';
      return;
    }
    const data = await response.json();
    // 更新组件中的用户状态
  } catch (err) {
    console.error('获取用户信息失败:', err);
  }
};

CSRF防护补充

sameSite: strict已能覆盖大部分CSRF场景,生产环境建议再加一层令牌验证:

  • 后端用csurf库生成CSRF令牌,存在Session中并返回给前端
  • 前端将令牌存在内存或LocalStorage中,非GET请求时携带在请求头
  • 后端验证请求头令牌与Session中是否一致

示例代码:
安装依赖:

npm install csurf

后端配置:

const csurf = require('csurf');
const csrfProtection = csurf({ cookie: true });

app.use(csrfProtection);

// 提供CSRF令牌的接口
app.get('/api/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// 登录接口加入CSRF验证
app.post('/api/login', csrfProtection, (req, res) => {
  // 登录逻辑...
});

前端处理:

// 页面加载时获取CSRF令牌
const getCsrfToken = async () => {
  const response = await fetch('/api/csrf-token', { credentials: 'include' });
  const data = await response.json();
  localStorage.setItem('csrfToken', data.csrfToken);
};

// 登录请求携带令牌
const handleLogin = async () => {
  const csrfToken = localStorage.getItem('csrfToken');
  try {
    const response = await fetch('/api/login', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
      },
      body: JSON.stringify({ username: 'test_user', password: '123456' })
    });
    // ...
  } catch (err) {
    console.error(err);
  }
};

为什么不推荐LocalStorage?

  • LocalStorage存储的数据可被前端JS直接读取,一旦页面存在XSS漏洞,攻击者能直接窃取会话信息
  • LocalStorage不会自动携带到请求中,需要手动处理,容易遗漏或出错
  • 无内置过期机制,需自行实现会话过期逻辑

内容的提问来源于stack exchange,提问作者Dalvin Segura

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 03:01:00