Skip to content

身份认证

身份认证用于确认请求主体;授权用于判断主体能否执行某项操作。认证信息通常放在 Cookie 或 Authorization: Bearer <token> 请求头中。

Session

Session 模式将会话状态保存到服务端,浏览器仅保存会话标识 Cookie。express-session 的默认内存存储仅适用于开发环境,生产环境应使用 Redis、数据库等持久化存储。

bash
pnpm add express-session
js
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 jsonwebtoken
js
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 })
})

密钥通过环境变量或密钥管理服务注入,不能硬编码在代码库。短期访问令牌通常配合可撤销的刷新令牌使用。

基于 MIT 许可发布