Node.js与Koa跨域Cookie共享实战指南
一、跨域Cookie的核心挑战
在前后端分离架构中,跨域请求(域名不同或端口不同)时浏览器默认会阻止Cookie的自动传递。这主要源于浏览器的同源策略(Same-Origin Policy),该策略要求协议、域名和端口三者完全一致才允许共享Cookie等敏感数据。
典型场景示例
- 前端:
https://client.example.com:8080 - 后端:
https://api.example.com:8443 - 需求:前端请求时自动携带认证Cookie
关键限制因素
- SameSite属性:现代浏览器默认将Cookie的SameSite属性设为Lax,严格限制跨站请求
- Secure标志:HTTPS环境下要求Cookie必须设置Secure标志
- HTTPOnly限制:涉及XSS防护时可能影响前端读取
二、Koa中间件配置方案
1. 基础CORS中间件配置
const Koa = require('koa');const cors = require('@koa/cors');const app = new Koa();app.use(cors({origin: function(ctx) {// 动态允许指定域名const allowedOrigins = ['https://client.example.com:8080','https://dev.client.example.com'];const requestOrigin = ctx.request.header.origin;return allowedOrigins.includes(requestOrigin) ? requestOrigin : false;},credentials: true, // 关键配置:允许携带认证信息allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],exposeHeaders: ['Content-Length', 'X-Kustom-Header'],maxAge: 600 // 预检请求缓存时间}));
2. 自定义中间件增强控制
app.use(async (ctx, next) => {// 设置响应头ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin);ctx.set('Access-Control-Allow-Credentials', 'true');ctx.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');// 处理OPTIONS预检请求if (ctx.method === 'OPTIONS') {ctx.status = 204;return;}await next();});
三、Cookie属性深度配置
1. 服务器端设置规范
app.use(async ctx => {// 设置可跨域的Cookiectx.cookies.set('session_id', 'abc123', {domain: '.example.com', // 允许子域名访问path: '/',secure: true, // 必须HTTPShttpOnly: true, // 防止XSS攻击sameSite: 'none', // 关键:允许跨站maxAge: 86400000 // 24小时有效期});ctx.body = { success: true };});
2. 属性组合策略
| 属性 | 推荐值 | 作用说明 |
|---|---|---|
| SameSite | none | 必须显式设置才能跨域 |
| Secure | true | 强制HTTPS传输 |
| HttpOnly | true | 防止JavaScript访问 |
| Domain | .example.com | 允许子域名共享 |
四、前端请求配置要点
1. Fetch API配置
fetch('https://api.example.com/data', {method: 'GET',credentials: 'include', // 关键配置:携带Cookieheaders: {'Content-Type': 'application/json'}}).then(response => response.json()).then(data => console.log(data));
2. Axios配置示例
const instance = axios.create({baseURL: 'https://api.example.com',withCredentials: true // 关键配置});instance.get('/data').then(response => console.log(response.data));
五、安全增强方案
1. CSRF防护机制
// 生成CSRF Token中间件app.use(async (ctx, next) => {const token = ctx.cookies.get('csrf_token') ||require('crypto').randomBytes(32).toString('hex');ctx.cookies.set('csrf_token', token, {secure: true,httpOnly: false, // 需要前端读取sameSite: 'strict'});await next();});// 验证中间件app.use(async (ctx, next) => {const method = ctx.method;if (['POST', 'PUT', 'DELETE'].includes(method)) {const token = ctx.request.body.csrf_token ||ctx.request.header['x-csrf-token'];if (token !== ctx.cookies.get('csrf_token')) {ctx.throw(403, 'Invalid CSRF token');}}await next();});
2. 动态域名白名单
const allowedDomains = new Set(['client.example.com','dev.client.example.com','staging.client.example.com']);app.use(async (ctx, next) => {const origin = ctx.request.header.origin;if (!origin) return await next();try {const url = new URL(origin);if (!allowedDomains.has(url.hostname)) {ctx.throw(403, 'Origin not allowed');}ctx.set('Access-Control-Allow-Origin', origin);} catch (e) {ctx.throw(400, 'Invalid origin');}await next();});
六、常见问题解决方案
1. 浏览器控制台报错”Credential is not supported”
原因:未同时设置credentials: true和sameSite: none+secure: true
解决方案:
// 确保中间件同时包含app.use(cors({origin: true, // 动态获取origincredentials: true}));// 设置Cookie时ctx.cookies.set('token', 'xxx', {sameSite: 'none',secure: true});
2. 子域名无法共享Cookie
原因:Domain属性设置错误
解决方案:
// 正确设置方式(注意前面的点)ctx.cookies.set('session', '123', {domain: '.example.com', // 允许所有子域名path: '/'});
七、性能优化建议
- 预检请求缓存:设置合理的
maxAge减少OPTIONS请求 - Cookie大小控制:保持Cookie在4KB以内,避免影响性能
- HTTP/2优化:启用HTTP/2减少头部开销
- Service Worker缓存:对静态资源使用Service Worker缓存
八、完整示例项目结构
project/├── src/│ ├── middleware/│ │ ├── cors.js # 自定义CORS中间件│ │ └── csrf.js # CSRF防护中间件│ ├── routes/│ │ └── api.js # API路由│ └── app.js # 主应用文件├── config/│ └── security.js # 安全配置└── package.json
九、生产环境部署检查清单
- 确认所有Cookie设置
secure: true - 验证CSRF防护机制有效
- 测试不同子域名的Cookie共享
- 检查HTTPS证书有效性
- 监控跨域请求的错误日志
- 定期更新CORS白名单
十、进阶方案:JWT替代方案
对于复杂场景,可考虑使用JWT替代Cookie:
// 生成Tokenconst jwt = require('jsonwebtoken');const token = jwt.sign({ userId: 123 }, 'secret', { expiresIn: '1h' });// 返回Tokenctx.body = { token };// 验证中间件app.use(async (ctx, next) => {const token = ctx.request.header.authorization?.split(' ')[1];try {const decoded = jwt.verify(token, 'secret');ctx.state.user = decoded;await next();} catch (e) {ctx.throw(401, 'Invalid token');}});
结语
实现跨域Cookie共享需要综合考虑浏览器安全策略、服务器配置和前端请求设置。通过合理配置Koa的CORS中间件、精确设置Cookie属性,并配合必要的安全防护机制,可以构建安全可靠的跨域认证系统。建议在实际项目中结合具体业务场景进行测试和调优,确保在安全性和用户体验之间取得平衡。