主题
身份认证
身份认证用于确认请求主体;授权用于判断主体能否执行某项操作。认证信息通常放在 Cookie 或 Authorization: Bearer <token> 请求头中。
Session
Session 模式将会话状态保存到服务端,浏览器仅保存会话标识 Cookie。express-session 的默认内存存储仅适用于开发环境,生产环境应使用 Redis、数据库等持久化存储。
bash
pnpm add express-sessionjs
import session from 'express-session'
app.set('trust proxy', 1) // 部署在受信任反向代理后且使用 secure Cookie 时需要
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 1000 * 60 * 60 * 24,
},
}),
)
app.post('/login', (req, res) => {
req.session.userId = 'user-id'
res.sendStatus(204)
})
app.post('/logout', (req, res, next) => {
req.session.destroy((error) => {
if (error) return next(error)
res.clearCookie('connect.sid')
res.sendStatus(204)
})
})JWT
JWT 是签名令牌,服务端应验证签名、过期时间、签发方和受众。令牌负载可被解码,不能存放密码等敏感信息。
bash
pnpm add jsonwebtokenjs
import jwt from 'jsonwebtoken'
const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET, {
algorithm: 'HS256',
expiresIn: '15m',
issuer: 'api.example.com',
audience: 'app.example.com',
})
function authenticate(req, res, next) {
const token = req.get('authorization')?.replace(/^Bearer\s+/, '')
if (!token) {
return res.status(401).json({ message: '缺少访问令牌' })
}
try {
req.auth = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'api.example.com',
audience: 'app.example.com',
})
next()
} catch {
res.status(401).json({ message: '访问令牌无效或已过期' })
}
}
app.get('/me', authenticate, (req, res) => {
res.json({ userId: req.auth.sub })
})密钥通过环境变量或密钥管理服务注入,不能硬编码在代码库。短期访问令牌通常配合可撤销的刷新令牌使用。
