name: session-management description: 使用JWT令牌、Redis存储、刷新流程和适当的Cookie配置实现安全的会话管理。适用于构建认证系统、管理用户会话或实现安全退出功能。
会话管理
实现安全的会话管理,包括适当的令牌处理和存储。
基于令牌的会话
const jwt = require('jsonwebtoken');
function generateTokens(user) {
const accessToken = jwt.sign(
{ userId: user.id, role: user.role, type: 'access' },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
const refreshToken = jwt.sign(
{ userId: user.id, type: 'refresh' },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
Redis会话存储
const redis = require('redis');
const client = redis.createClient();
class SessionStore {
async create(userId, sessionData) {
const sessionId = crypto.randomUUID();
await client.hSet(`sessions:${userId}`, sessionId, JSON.stringify({
...sessionData,
createdAt: Date.now()
}));
await client.expire(`sessions:${userId}`, 86400 * 7);
return sessionId;
}
async invalidateAll(userId) {
await client.del(`sessions:${userId}`);
}
}
Cookie配置
app.use(session({
name: 'session',
secret: process.env.SESSION_SECRET,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 3600000, // 1小时
domain: '.example.com'
},
resave: false,
saveUninitialized: false
}));
令牌刷新流程
app.post('/auth/refresh', async (req, res) => {
const { refreshToken } = req.cookies;
try {
const payload = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
if (payload.type !== 'refresh') throw new Error('Invalid token type');
const user = await User.findById(payload.userId);
const tokens = generateTokens(user);
res.cookie('accessToken', tokens.accessToken, cookieOptions);
res.json({ success: true });
} catch (err) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
安全要求
- 专门使用HTTPS
- 在Cookie上设置httpOnly和sameSite
- 实现适当的令牌过期
- 使用强、唯一的秘密密钥(每个环境)
- 在每个请求上验证签名
切勿做
- 在令牌中存储敏感数据
- 通过URL参数传输令牌
- 使用弱或共享的秘密密钥
- 跳过签名验证