阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > Cookie与Session管理

Cookie与Session管理

作者:陈川 阅读数:12573人阅读 分类: Node.js

Cookie与Session的基本概念

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Session则是服务器端用来存储用户会话信息的机制,通常基于Cookie实现。

// 设置一个简单的Cookie
res.cookie('username', 'john_doe', { maxAge: 900000, httpOnly: true });

Express中的Cookie管理

Express通过cookie-parser中间件简化了Cookie操作。首先需要安装并引入这个中间件:

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

app.use(cookieParser());

设置Cookie时可以指定多个选项:

res.cookie('token', 'abc123xyz', {
  maxAge: 24 * 60 * 60 * 1000, // 1天
  secure: true, // 仅HTTPS
  httpOnly: true, // 防止XSS
  sameSite: 'strict' // CSRF防护
});

读取Cookie非常简单:

app.get('/', (req, res) => {
  const userToken = req.cookies.token;
  // 使用token进行验证
});

删除Cookie的方法:

res.clearCookie('token');

Session管理实现方案

Express本身不直接支持Session,需要借助中间件。常用方案有:

  1. 内存Session(开发环境使用)
const session = require('express-session');
app.use(session({
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: true
}));
  1. Redis存储Session(生产环境推荐)
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
const redisClient = redis.createClient();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: false,
  cookie: { secure: true }
}));

Session的典型使用场景

用户登录状态维护:

// 登录时设置Session
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // 验证逻辑...
  req.session.user = {
    id: user.id,
    username: user.username,
    role: user.role
  };
  res.redirect('/dashboard');
});

// 检查Session中间件
function checkAuth(req, res, next) {
  if (!req.session.user) {
    return res.status(401).send('请先登录');
  }
  next();
}

// 受保护的路由
app.get('/profile', checkAuth, (req, res) => {
  res.render('profile', { user: req.session.user });
});

购物车实现示例:

// 添加商品到购物车
app.post('/cart/add', (req, res) => {
  if (!req.session.cart) {
    req.session.cart = [];
  }
  req.session.cart.push(req.body.product);
  res.json({ success: true });
});

// 获取购物车内容
app.get('/cart', (req, res) => {
  res.json(req.session.cart || []);
});

安全最佳实践

  1. Cookie安全设置
app.use(session({
  // ...
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 24 * 60 * 60 * 1000
  }
}));
  1. Session固定攻击防护
app.use(session({
  // ...
  genid: (req) => {
    return crypto.randomUUID(); // 使用强随机ID
  }
}));
  1. 定期更换Session密钥
app.use(session({
  secret: [currentSecret, oldSecret],
  rolling: true // 每次请求刷新过期时间
}));

性能优化技巧

  1. Session存储优化
app.use(session({
  // ...
  saveUninitialized: false, // 不保存未修改的session
  resave: false, // 避免不必要的重写
  cookie: {
    maxAge: 30 * 60 * 1000 // 30分钟不活动则过期
  }
}));
  1. 分布式Session管理
// 使用MongoDB存储Session
const MongoStore = require('connect-mongo');
app.use(session({
  store: MongoStore.create({
    mongoUrl: 'mongodb://localhost/session_db',
    ttl: 14 * 24 * 60 * 60 // 14天
  }),
  // ...
}));
  1. 减少Session数据量
// 只存储必要信息
req.session.user = {
  id: user.id, // 只存ID,其他信息从数据库查询
  lastActive: Date.now()
};

常见问题解决方案

  1. Cookie大小限制问题
// 对于大数据,考虑使用数据库引用
req.session.largeDataRef = 'data_id';
// 实际数据存储在数据库
  1. 跨域Session共享
app.use(session({
  // ...
  cookie: {
    domain: '.example.com', // 允许子域名共享
    path: '/'
  }
}));
  1. 移动端Session处理
// 对于API请求,可以使用Token代替Cookie
app.post('/api/login', (req, res) => {
  // 验证成功
  const token = generateToken(user);
  res.json({ token });
  
  // 或者仍然使用Cookie但配置移动端友好
  res.cookie('session', token, {
    httpOnly: true,
    sameSite: 'none',
    secure: true
  });
});

高级应用场景

  1. 多设备Session管理
// 在Session中存储设备信息
req.session.devices = req.session.devices || [];
req.session.devices.push({
  id: deviceId,
  ip: req.ip,
  userAgent: req.headers['user-agent'],
  lastActive: new Date()
});

// 实现设备管理功能
app.get('/account/devices', (req, res) => {
  res.render('devices', { devices: req.session.devices });
});

app.post('/account/devices/revoke', (req, res) => {
  req.session.devices = req.session.devices.filter(
    d => d.id !== req.body.deviceId
  );
  res.redirect('/account/devices');
});
  1. 实时权限变更
// 当管理员修改用户权限时
function updateUserPermissions(userId, newRole) {
  // 更新数据库
  // 然后更新所有活跃Session
  sessionStore.all((err, sessions) => {
    for (const sessionId in sessions) {
      if (sessions[sessionId].user.id === userId) {
        sessions[sessionId].user.role = newRole;
        sessionStore.set(sessionId, sessions[sessionId]);
      }
    }
  });
}
  1. 长期会话与短期会话结合
// 常规会话(短期)
app.use(session({
  name: 'session',
  secret: 'short_term_secret',
  cookie: { maxAge: 30 * 60 * 1000 } // 30分钟
}));

// 记住我功能(长期)
app.post('/login', (req, res) => {
  // 验证逻辑...
  if (req.body.rememberMe) {
    const longTermToken = generateToken();
    res.cookie('remember_me', longTermToken, {
      maxAge: 30 * 24 * 60 * 60 * 1000, // 30天
      httpOnly: true,
      secure: true
    });
    // 将token存入数据库关联用户
  }
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌