跳到主要内容

Cookie 与 Session

问题

Cookie 和 Session 的区别是什么?如何实现用户登录状态管理?

答案

Cookie 和 Session 都是用于维护用户状态的机制,但存储位置和工作方式不同。


基本概念

特性CookieSession
存储位置客户端(浏览器)服务端
存储大小约 4KB无限制(服务端内存)
安全性较低(可被篡改)较高
生命周期可设置过期时间默认关闭浏览器失效
跨域同域下共享不可跨服务器

// 设置 Cookie
document.cookie = 'name=value; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/; domain=.example.com; secure; samesite=strict';
属性说明示例
name=valueCookie 名称和值userId=123
expires过期时间(绝对时间)Fri, 31 Dec 2024 23:59:59 GMT
max-age过期时间(秒数)86400(1 天)
path有效路径/admin
domain有效域名.example.com
secure仅 HTTPS 传输无值
httpOnly禁止 JS 访问无值
sameSite跨站限制strict/lax/none
// 工具类封装
class CookieUtil {
// 设置 Cookie
static set(
name: string,
value: string,
options: {
expires?: Date;
maxAge?: number;
path?: string;
domain?: string;
secure?: boolean;
sameSite?: 'strict' | 'lax' | 'none';
} = {}
): void {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;

if (options.expires) {
cookie += `; expires=${options.expires.toUTCString()}`;
}
if (options.maxAge !== undefined) {
cookie += `; max-age=${options.maxAge}`;
}
if (options.path) {
cookie += `; path=${options.path}`;
}
if (options.domain) {
cookie += `; domain=${options.domain}`;
}
if (options.secure) {
cookie += '; secure';
}
if (options.sameSite) {
cookie += `; samesite=${options.sameSite}`;
}

document.cookie = cookie;
}

// 获取 Cookie
static get(name: string): string | null {
const cookies = document.cookie.split('; ');
for (const cookie of cookies) {
const [key, value] = cookie.split('=');
if (decodeURIComponent(key) === name) {
return decodeURIComponent(value);
}
}
return null;
}

// 删除 Cookie
static remove(name: string, path = '/'): void {
this.set(name, '', { maxAge: -1, path });
}
}

// 使用示例
CookieUtil.set('token', 'abc123', {
maxAge: 7 * 24 * 3600, // 7 天
path: '/',
secure: true,
sameSite: 'strict'
});

const token = CookieUtil.get('token');
CookieUtil.remove('token');

SameSite 属性

// SameSite 防止 CSRF 攻击

// Strict:完全禁止跨站请求携带 Cookie
// 从其他网站点击链接到你的网站,不会携带 Cookie
Set-Cookie: session=abc; SameSite=Strict

// Lax(Chrome 默认):允许导航的 GET 请求携带
// 从其他网站点击链接可以,但表单提交、Ajax 不行
Set-Cookie: session=abc; SameSite=Lax

// None:允许跨站(必须配合 Secure)
Set-Cookie: session=abc; SameSite=None; Secure

Session 详解

工作流程

Node.js Session 实现

import express from 'express';
import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const app = express();

// Redis 客户端(生产环境推荐)
const redisClient = createClient({
url: 'redis://localhost:6379'
});
await redisClient.connect();

// 配置 Session
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
name: 'sessionId', // Cookie 名称
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000, // 1 天
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
}
}));

// 登录接口
app.post('/login', (req, res) => {
const { username, password } = req.body;

// 验证用户...

// 存储 Session
req.session.userId = 123;
req.session.username = username;

res.json({ success: true });
});

// 获取用户信息
app.get('/user', (req, res) => {
if (req.session.userId) {
res.json({
userId: req.session.userId,
username: req.session.username
});
} else {
res.status(401).json({ error: 'Unauthorized' });
}
});

// 登出
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
res.clearCookie('sessionId');
res.json({ success: true });
});
});

Session 存储方案

存储方式优点缺点适用场景
内存快速、简单重启丢失、不能共享开发环境
文件简单性能差、不能共享小型应用
数据库持久化、可查询性能一般需要分析
Redis快速、可共享、可过期需要维护生产环境

对比项CookieSession
存储位置浏览器服务器
存储容量4KB无限制
安全性
服务器压力占用内存
跨服务器可通过域名共享需要共享存储
适用场景偏好设置、广告追踪登录状态、购物车

安全最佳实践

HttpOnly 防止 XSS

// 服务端设置
res.cookie('sessionId', 'abc123', {
httpOnly: true // JavaScript 无法访问
});

// 攻击者无法通过 document.cookie 获取

Secure 防止中间人攻击

// 仅 HTTPS 传输
res.cookie('sessionId', 'abc123', {
secure: true
});

SameSite 防止 CSRF

// 限制跨站请求携带 Cookie
res.cookie('sessionId', 'abc123', {
sameSite: 'strict'
});

完整安全配置

// 生产环境 Cookie 配置
const cookieOptions = {
httpOnly: true, // 防止 XSS
secure: true, // 仅 HTTPS
sameSite: 'strict', // 防止 CSRF
maxAge: 3600000, // 1 小时
signed: true // 签名防篡改
};

前端配置

// fetch 携带 Cookie
fetch('https://api.example.com/user', {
credentials: 'include' // 关键配置
});

// axios 配置
axios.defaults.withCredentials = true;

后端配置

// Express CORS 配置
import cors from 'cors';

app.use(cors({
origin: 'https://www.example.com', // 不能使用 *
credentials: true
}));

// Cookie 跨域设置
res.cookie('token', 'abc', {
sameSite: 'none', // 必须
secure: true // 必须
});

常见面试问题

答案

区别CookieSession
存储位置浏览器服务器
安全性低(可篡改)
存储大小4KB无限制
生命周期可设置过期默认会话级
服务器压力占用资源

工作关系:Session 通常依赖 Cookie 存储 SessionId。

答案

// 1. HttpOnly - 防止 XSS 窃取
Set-Cookie: sessionId=abc; HttpOnly

// 2. Secure - 仅 HTTPS 传输
Set-Cookie: sessionId=abc; Secure

// 3. SameSite - 防止 CSRF
Set-Cookie: sessionId=abc; SameSite=Strict

// 4. 签名 - 防止篡改
// 使用 cookie-signature 库签名

// 5. 定期更换 SessionId
// 登录后重新生成

Q3: 分布式 Session 如何处理?

答案

推荐方案:Redis 集中存储

// 所有服务器连接同一个 Redis
import RedisStore from 'connect-redis';

app.use(session({
store: new RedisStore({ client: redisClient }),
// ...
}));

答案

防止 CSRF(跨站请求伪造)攻击。

行为场景
Strict完全禁止跨站携带银行、支付
Lax允许导航 GET 请求普通网站(默认)
None允许跨站(需 Secure)第三方服务、广告

答案

  1. 数据位置:Session 数据存在服务端,用户无法查看和修改
  2. 传输内容:只传输 SessionId,敏感数据不暴露
  3. 可控性:服务端可随时使 Session 失效
  4. 防篡改:SessionId 可加密签名
// Cookie 方式(不安全)
// 用户信息暴露在浏览器
document.cookie = 'userId=123; role=admin';
// 可被篡改

// Session 方式(安全)
// 只传输 SessionId
document.cookie = 'sessionId=abc123';
// 用户信息存在服务端,无法篡改

相关链接