{T}

高安全架构 学习笔记(第 9 部分)

5.2 JWT 认证实现

javascript
// JWT 认证系统
const jwt = require('jsonwebtoken')
 
class JWTAuthentication {
  constructor() {
    this.secret = 'your-secret-key'
    this.expiresIn = '7d'
    this.refreshExpiresIn = '30d'
  }
  
  // 生成访问令牌
  generateAccessToken(user) {
    const payload = {
      userId: user.id,
      username: user.username,
      role: user.role,
      type: 'access'
    }
    
    return jwt.sign(payload, this.secret, {
      expiresIn: this.expiresIn
    })
  }
  
  // 生成刷新令牌
  generateRefreshToken(user) {
    const payload = {
      userId: user.id,
      type: 'refresh'
    }
    
    return jwt.sign(payload, this.secret, {
      expiresIn: this.refreshExpiresIn
    })
  }
  
  // 验证令牌
  verifyToken(token) {
    try {
      const decoded = jwt.verify(token, this.secret)
      
      return {
        valid: true,
        decoded
      }
    } catch (error) {
      return {
        valid: false,
        error: error.message
      }
    }
  }
  
  // 刷新令牌
  refreshToken(refreshToken) {
    const result = this.verifyToken(refreshToken)
    
    if (!result.valid || result.decoded.type !== 'refresh') {
      throw new Error('无效的刷新令牌')
    }
    
    // 获取用户信息
    const user = this.getUserById(result.decoded.userId)
    
    // 生成新的访问令牌
    const newAccessToken = this.generateAccessToken(user)
    
    return {
      accessToken: newAccessToken,
      expiresIn: this.expiresIn
    }
  }
  
  // 认证中间件
  authenticate() {
    return (req, res, next) => {
      const token = this.extractToken(req)
      
      if (!token) {
        return res.status(401).json({
          error: 'Unauthorized',
          message: '未提供认证令牌'
        })
      }
      
      const result = this.verifyToken(token)
      
      if (!result.valid) {
        return res.status(401).json({
          error: 'Unauthorized',
          message: '令牌无效或已过期'
        })
      }
      
      // 将用户信息附加到请求对象
      req.user = result.decoded
      
      next()
    }
  }
  
  // 提取令牌
  extractToken(req) {
    // 从 Authorization 头提取
    const authHeader = req.headers.authorization
    
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.substring(7)
    }
    
    // 从查询参数提取
    if (req.query.token) {
      return req.query.token
    }
    
    // 从 Cookie 提取
    if (req.cookies && req.cookies.token) {
      return req.cookies.token
    }
    
    return null
  }
  
  // 获取用户信息
  getUserById(userId) {
    // 实际实现:从数据库查询
    return {
      id: userId,
      username: 'user123',
      role: 'user'
    }
  }
}
 
// 使用示例
const auth = new JWTAuthentication()
 
// 登录时生成令牌
app.post('/login', (req, res) => {
  const { username, password } = req.body
  
  // 验证用户名密码
  const user = authenticateUser(username, password)
  
  if (!user) {
    return res.status(401).json({ error: '用户名或密码错误' })
  }
  
  // 生成令牌
  const accessToken = auth.generateAccessToken(user)
  const refreshToken = auth.generateRefreshToken(user)
  
  res.json({
    accessToken,
    refreshToken,
    expiresIn: auth.expiresIn
  })
})
 
// 受保护的路由
app.get('/protected', auth.authenticate(), (req, res) => {
  res.json({
    message: '访问成功',
    user: req.user
  })
})