🚀 全新升级 - Nuxt4 AI企业级开发实战指南现已发布!

中间件与权限控制实践指南和技术原理

深入解析 Nuxt 框架中间件系统的技术架构和工作原理,涵盖全局中间件、路由中间件、服务端中间件的实现机制,以及 JWT 认证、OAuth2 集成、权限控制策略等实践指南,构建安全可靠的企业级应用

概述

在现代 Web 应用开发中,安全性和权限控制是保障业务数据和用户隐私的核心要素。据 OWASP 2023 报告显示,身份验证和权限管理问题仍位居 Web 应用十大安全风险之列。Nuxt 框架基于强大的中间件系统,提供了完整的权限控制解决方案,从路由保护到 API 安全,从客户端验证到服务端鉴权。

本指南将深入探讨 Nuxt 中间件系统的技术架构和实现原理,帮助开发者构建安全、高效的权限控制体系。

🎯 核心目标

  • 深入理解 Nuxt 中间件系统的技术架构和执行机制
  • 掌握全局中间件、路由中间件、服务端中间件的最佳实践
  • 学会构建完整的认证授权体系(JWT、OAuth2、RBAC)
  • 掌握 Web 安全防护技术(CSRF、XSS、请求限制防护)
  • 学会设计企业级权限控制架构和监控体系

💡 技术架构体系

  • Nitro 引擎: 统一的服务端运行时和中间件执行环境
  • Vue Router: 客户端路由保护和导航守卫机制
  • H3: 轻量级 HTTP 框架和中间件系统
  • unjs 生态: jose、ofetch、unstorage 等安全工具链
  • 边缘计算: Edge Runtime 和分布式认证策略

第一部分:中间件系统深度解析

8.1 中间件类型与执行机制

Nuxt 3 基于 Nitro 引擎提供了三种类型的中间件,每种类型都有特定的执行时机和适用场景。

8.1.1 全局中间件

全局中间件在每次路由变化时自动执行,是实现全站权限控制的核心机制。

技术原理与执行时机

// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to) => {
  console.log('全局中间件执行时机:', {
    执行阶段: '路由导航前',
    执行环境: process.client ? '客户端' : '服务端',
    目标路由: to.path,
    执行时间: Date.now()
  })
  
  // 获取用户认证状态
  const { $auth } = useNuxtApp()
  const user = useAuthUser()
  
  // 白名单路由检查
  const publicRoutes = ['/login', '/register', '/forgot-password', '/']
  const isPublicRoute = publicRoutes.some(route => 
    to.path.startsWith(route)
  )
  
  // 权限验证逻辑
  if (!isPublicRoute && !user.value) {
    console.warn('未授权访问尝试:', {
      targetRoute: to.path,
      userAgent: process.client ? navigator.userAgent : 'SSR',
      timestamp: new Date().toISOString()
    })
    
    // 保存目标路由用于登录后重定向
    if (process.client) {
      localStorage.setItem('redirectAfterAuth', to.fullPath)
    }
    
    return navigateTo('/login')
  }
})

高级应用场景

// middleware/security.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  // 安全策略检查
  const securityChecks = {
    // 1. 检查恶意路由模式
    validateRouteSecurity: () => {
      const suspiciousPatterns = [
        /\.\.\//, // 路径遍历攻击
        /<script/, // XSS 攻击模式
        /union.*select/i, // SQL 注入模式
      ]
      
      return !suspiciousPatterns.some(pattern => 
        pattern.test(to.path)
      )
    },
    
    // 2. 检查访问频率限制
    validateRateLimit: () => {
      if (process.client) {
        const requestLog = JSON.parse(
          localStorage.getItem('requestLog') || '[]'
        )
        const now = Date.now()
        const recentRequests = requestLog.filter(
          (timestamp: number) => now - timestamp < 60000 // 1分钟内
        )
        
        if (recentRequests.length > 100) {
          console.warn('检测到异常访问频率')
          return false
        }
        
        requestLog.push(now)
        localStorage.setItem('requestLog', JSON.stringify(
          requestLog.slice(-50) // 保留最近50条记录
        ))
      }
      return true
    },
    
    // 3. 检查设备指纹
    validateDeviceFingerprint: () => {
      if (process.client && from) {
        const deviceInfo = {
          userAgent: navigator.userAgent,
          screen: `${screen.width}x${screen.height}`,
          timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
          language: navigator.language
        }
        
        const fingerprint = btoa(JSON.stringify(deviceInfo))
        const storedFingerprint = localStorage.getItem('deviceFingerprint')
        
        if (storedFingerprint && storedFingerprint !== fingerprint) {
          console.warn('设备指纹变化检测')
          // 可以触发额外验证或登出
        } else {
          localStorage.setItem('deviceFingerprint', fingerprint)
        }
      }
      return true
    }
  }
  
  // 执行安全检查
  const securityPassed = Object.values(securityChecks).every(check => check())
  
  if (!securityPassed) {
    throw createError({
      statusCode: 403,
      statusMessage: '安全检查失败',
      data: { timestamp: new Date().toISOString() }
    })
  }
})

8.1.2 路由中间件

路由中间件提供更细粒度的权限控制,可以针对特定页面或页面组实现定制化的权限验证。

基础权限验证中间件

// middleware/admin.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useAuthUser()
  
  // 角色权限检查
  if (!user.value || !user.value.roles?.includes('admin')) {
    throw createError({
      statusCode: 403,
      statusMessage: '需要管理员权限才能访问此页面'
    })
  }
})

// middleware/subscription.ts
export default defineNuxtRouteMiddleware((to) => {
  const user = useAuthUser()
  
  // 订阅状态检查
  if (!user.value?.subscription?.active) {
    return navigateTo('/upgrade', {
      query: { return: to.fullPath }
    })
  }
})

高级权限策略实现

// middleware/rbac.ts
interface Permission {
  resource: string
  action: string
  conditions?: Record<string, any>
}

interface Role {
  name: string
  permissions: Permission[]
  inheritance?: string[]
}

export default defineNuxtRouteMiddleware((to) => {
  const user = useAuthUser()
  const { checkPermission } = useRBAC()
  
  // 从路由元信息获取所需权限
  const requiredPermissions = to.meta.permissions as Permission[]
  
  if (!requiredPermissions || requiredPermissions.length === 0) {
    return // 无权限要求的路由
  }
  
  // RBAC 权限检查
  const hasPermission = requiredPermissions.every(permission => 
    checkPermission(user.value, permission, {
      context: {
        route: to.path,
        params: to.params,
        query: to.query
      }
    })
  )
  
  if (!hasPermission) {
    console.warn('RBAC 权限检查失败:', {
      user: user.value?.id,
      route: to.path,
      requiredPermissions,
      userRoles: user.value?.roles,
      timestamp: new Date().toISOString()
    })
    
    throw createError({
      statusCode: 403,
      statusMessage: '权限不足,无法访问此资源',
      data: {
        requiredPermissions,
        userPermissions: user.value?.permissions || []
      }
    })
  }
})

页面级权限定义

<!-- pages/admin/users.vue -->
<template>
  <div>
    <h1>用户管理</h1>
    <!-- 用户管理界面 -->
  </div>
</template>

<script setup lang="ts">
// 定义页面权限要求
definePageMeta({
  middleware: ['auth', 'rbac'],
  permissions: [
    { resource: 'users', action: 'read' },
    { resource: 'users', action: 'list' }
  ],
  layout: 'admin'
})
</script>

8.1.3 服务端中间件

服务端中间件运行在 Nitro 引擎中,负责 API 路由的安全保护和请求处理。

API 认证中间件

// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
  // 只处理 API 路由
  if (!event.node.req.url?.startsWith('/api/')) {
    return
  }
  
  // 跳过公开 API
  const publicEndpoints = [
    '/api/auth/login',
    '/api/auth/register',
    '/api/auth/refresh',
    '/api/health'
  ]
  
  if (publicEndpoints.some(endpoint => 
    event.node.req.url?.startsWith(endpoint)
  )) {
    return
  }
  
  try {
    // 获取授权令牌
    const authorization = getHeader(event, 'authorization')
    if (!authorization?.startsWith('Bearer ')) {
      throw createError({
        statusCode: 401,
        statusMessage: '缺少有效的授权令牌'
      })
    }
    
    const token = authorization.slice(7)
    
    // JWT 令牌验证
    const { verifyJWT } = useJWT()
    const payload = await verifyJWT(token)
    
    // 令牌黑名单检查
    const { isTokenBlacklisted } = useTokenBlacklist()
    if (await isTokenBlacklisted(token)) {
      throw createError({
        statusCode: 401,
        statusMessage: '令牌已被吊销'
      })
    }
    
    // 用户状态检查
    const user = await getUserById(payload.userId)
    if (!user || !user.active) {
      throw createError({
        statusCode: 401,
        statusMessage: '用户账户已被禁用'
      })
    }
    
    // 将用户信息添加到请求上下文
    event.context.user = user
    event.context.tokenPayload = payload
    
  } catch (error) {
    console.error('API 认证失败:', {
      url: event.node.req.url,
      method: event.node.req.method,
      ip: getClientIP(event),
      userAgent: getHeader(event, 'user-agent'),
      error: error.message,
      timestamp: new Date().toISOString()
    })
    
    throw createError({
      statusCode: 401,
      statusMessage: '认证失败'
    })
  }
})

第二部分:认证与授权体系

8.2.1 JWT 认证实现

JSON Web Token (JWT) 是现代 Web 应用中最常用的认证机制,具有无状态、跨域友好、性能优秀等特点。

JWT 工具函数实现

// composables/useJWT.ts
import * as jose from 'jose'

export const useJWT = () => {
  const runtimeConfig = useRuntimeConfig()
  
  // JWT 密钥管理
  const getSecret = () => {
    const secret = runtimeConfig.jwtSecret
    if (!secret) {
      throw new Error('JWT_SECRET 环境变量未配置')
    }
    return new TextEncoder().encode(secret)
  }
  
  // 生成 JWT 令牌
  const generateJWT = async (payload: any, options: {
    expiresIn?: string
    audience?: string
    issuer?: string
  } = {}) => {
    const secret = getSecret()
    
    const jwt = await new jose.SignJWT(payload)
      .setProtectedHeader({ alg: 'HS256' })
      .setIssuedAt()
      .setIssuer(options.issuer || 'nuxt-app')
      .setAudience(options.audience || 'nuxt-app-users')
      .setExpirationTime(options.expiresIn || '1h')
      .sign(secret)
    
    return jwt
  }
  
  // 验证 JWT 令牌
  const verifyJWT = async (token: string) => {
    try {
      const secret = getSecret()
      const { payload } = await jose.jwtVerify(token, secret, {
        issuer: 'nuxt-app',
        audience: 'nuxt-app-users'
      })
      
      return payload
    } catch (error) {
      console.error('JWT 验证失败:', error.message)
      throw new Error('无效的令牌')
    }
  }
  
  // 刷新令牌
  const refreshToken = async (token: string) => {
    try {
      const payload = await verifyJWT(token)
      
      // 检查令牌是否接近过期(剩余时间少于 15 分钟)
      const exp = payload.exp as number
      const now = Math.floor(Date.now() / 1000)
      
      if (exp - now > 15 * 60) {
        return token // 令牌仍然有效
      }
      
      // 生成新令牌
      const newPayload = {
        userId: payload.userId,
        email: payload.email,
        roles: payload.roles
      }
      
      return await generateJWT(newPayload)
    } catch (error) {
      throw new Error('令牌刷新失败')
    }
  }
  
  // 解码令牌(不验证签名)
  const decodeJWT = (token: string) => {
    try {
      return jose.decodeJwt(token)
    } catch {
      return null
    }
  }
  
  return {
    generateJWT,
    verifyJWT,
    refreshToken,
    decodeJWT
  }
}

认证服务实现

// server/api/auth/login.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const { email, password, rememberMe } = body
  
  // 输入验证
  if (!email || !password) {
    throw createError({
      statusCode: 400,
      statusMessage: '邮箱和密码不能为空'
    })
  }
  
  try {
    // 用户认证
    const user = await authenticateUser(email, password)
    
    if (!user) {
      // 记录登录失败日志
      await logAuthEvent({
        type: 'login_failed',
        email,
        ip: getClientIP(event),
        userAgent: getHeader(event, 'user-agent'),
        reason: 'invalid_credentials'
      })
      
      throw createError({
        statusCode: 401,
        statusMessage: '邮箱或密码错误'
      })
    }
    
    // 检查账户状态
    if (!user.active) {
      throw createError({
        statusCode: 403,
        statusMessage: '账户已被禁用'
      })
    }
    
    // 生成令牌
    const { generateJWT } = useJWT()
    const tokenPayload = {
      userId: user.id,
      email: user.email,
      roles: user.roles,
      permissions: user.permissions
    }
    
    const accessToken = await generateJWT(tokenPayload, {
      expiresIn: rememberMe ? '30d' : '1h'
    })
    
    const refreshToken = await generateJWT(
      { userId: user.id, type: 'refresh' },
      { expiresIn: '30d' }
    )
    
    // 更新用户登录信息
    await updateUserLastLogin(user.id, {
      lastLoginAt: new Date(),
      lastLoginIp: getClientIP(event),
      lastLoginUserAgent: getHeader(event, 'user-agent')
    })
    
    // 记录登录成功日志
    await logAuthEvent({
      type: 'login_success',
      userId: user.id,
      email: user.email,
      ip: getClientIP(event),
      userAgent: getHeader(event, 'user-agent')
    })
    
    // 设置安全 Cookie
    setCookie(event, 'auth-token', accessToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: rememberMe ? 30 * 24 * 60 * 60 : 60 * 60, // 30天或1小时
      path: '/'
    })
    
    setCookie(event, 'refresh-token', refreshToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: 30 * 24 * 60 * 60, // 30天
      path: '/api/auth'
    })
    
    return {
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
        roles: user.roles,
        avatar: user.avatar
      },
      accessToken,
      expiresIn: rememberMe ? 30 * 24 * 60 * 60 : 60 * 60
    }
    
  } catch (error) {
    console.error('登录过程出错:', error)
    
    if (error.statusCode) {
      throw error
    }
    
    throw createError({
      statusCode: 500,
      statusMessage: '登录服务暂时不可用'
    })
  }
})

8.2.2 OAuth2 集成

OAuth2 是业界标准的授权框架,支持第三方登录和 API 访问授权。

OAuth2 提供商配置

// server/api/auth/oauth/[provider].get.ts
export default defineEventHandler(async (event) => {
  const provider = getRouterParam(event, 'provider')
  const query = getQuery(event)
  
  const oauthConfig = {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      redirectUri: `${process.env.BASE_URL}/api/auth/oauth/google/callback`,
      scope: 'openid email profile',
      authUrl: 'https://accounts.google.com/o/oauth2/v2/auth'
    },
    github: {
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET,
      redirectUri: `${process.env.BASE_URL}/api/auth/oauth/github/callback`,
      scope: 'user:email',
      authUrl: 'https://github.com/login/oauth/authorize'
    }
  }
  
  const config = oauthConfig[provider as keyof typeof oauthConfig]
  if (!config) {
    throw createError({
      statusCode: 400,
      statusMessage: '不支持的 OAuth 提供商'
    })
  }
  
  // 生成 state 参数防止 CSRF
  const state = generateRandomString(32)
  
  // 存储 state 到会话中
  await setSessionData(event, 'oauth_state', state)
  
  // 构建授权 URL
  const authUrl = new URL(config.authUrl)
  authUrl.searchParams.set('client_id', config.clientId)
  authUrl.searchParams.set('redirect_uri', config.redirectUri)
  authUrl.searchParams.set('scope', config.scope)
  authUrl.searchParams.set('response_type', 'code')
  authUrl.searchParams.set('state', state)
  
  if (provider === 'google') {
    authUrl.searchParams.set('access_type', 'offline')
  }
  
  return sendRedirect(event, authUrl.toString())
})

第三部分:安全性增强

8.3.1 CSRF 防护

跨站请求伪造(CSRF)是一种常见的 Web 安全威胁,需要通过多种机制进行防护。

CSRF Token 生成与验证

// server/utils/csrf.ts
import { randomBytes, createHmac } from 'crypto'

export class CSRFProtection {
  private secret: string
  
  constructor() {
    this.secret = process.env.CSRF_SECRET || 'default-csrf-secret'
  }
  
  // 生成 CSRF Token
  generateToken(sessionId: string): string {
    const timestamp = Date.now().toString()
    const randomValue = randomBytes(16).toString('hex')
    const payload = `${sessionId}:${timestamp}:${randomValue}`
    
    const signature = createHmac('sha256', this.secret)
      .update(payload)
      .digest('hex')
    
    return Buffer.from(`${payload}:${signature}`).toString('base64')
  }
  
  // 验证 CSRF Token
  verifyToken(token: string, sessionId: string): boolean {
    try {
      const decoded = Buffer.from(token, 'base64').toString('utf8')
      const [session, timestamp, random, signature] = decoded.split(':')
      
      // 检查会话 ID
      if (session !== sessionId) {
        return false
      }
      
      // 检查时间戳(token 有效期 1 小时)
      const tokenTime = parseInt(timestamp)
      const now = Date.now()
      if (now - tokenTime > 60 * 60 * 1000) {
        return false
      }
      
      // 验证签名
      const payload = `${session}:${timestamp}:${random}`
      const expectedSignature = createHmac('sha256', this.secret)
        .update(payload)
        .digest('hex')
      
      return signature === expectedSignature
    } catch {
      return false
    }
  }
}

const csrfProtection = new CSRFProtection()
export { csrfProtection }

8.3.2 XSS 防护

跨站脚本攻击(XSS)是最常见的 Web 安全威胁之一,需要通过输入验证、输出编码、CSP 等多层防护。

输入验证与清理

// server/utils/xss-protection.ts
import DOMPurify from 'isomorphic-dompurify'
import validator from 'validator'

export class XSSProtection {
  // HTML 内容清理
  static sanitizeHTML(input: string, options: {
    allowedTags?: string[]
    allowedAttributes?: Record<string, string[]>
    strict?: boolean
  } = {}): string {
    if (!input || typeof input !== 'string') {
      return ''
    }
    
    const defaultConfig = {
      ALLOWED_TAGS: options.allowedTags || [
        'p', 'br', 'strong', 'em', 'u', 'ul', 'ol', 'li', 'h1', 'h2', 'h3'
      ],
      ALLOWED_ATTR: options.allowedAttributes || {
        'a': ['href', 'title'],
        'img': ['src', 'alt', 'title'],
        '*': ['class']
      },
      FORBID_SCRIPT: true,
      FORBID_TAGS: ['script', 'object', 'embed', 'form', 'input'],
      FORBID_ATTR: ['onclick', 'onload', 'onerror', 'onfocus']
    }
    
    if (options.strict) {
      defaultConfig.ALLOWED_TAGS = ['p', 'br', 'strong', 'em']
      defaultConfig.ALLOWED_ATTR = {}
    }
    
    return DOMPurify.sanitize(input, defaultConfig)
  }
  
  // 文本内容转义
  static escapeHTML(input: string): string {
    if (!input || typeof input !== 'string') {
      return ''
    }
    
    return validator.escape(input)
  }
  
  // 检测潜在的 XSS 攻击
  static detectXSSAttempt(input: string): boolean {
    if (!input || typeof input !== 'string') {
      return false
    }
    
    const xssPatterns = [
      /<script[\s\S]*?>/i,
      /javascript:/i,
      /on\w+\s*=/i,
      /<iframe[\s\S]*?>/i,
      /<object[\s\S]*?>/i,
      /<embed[\s\S]*?>/i,
      /expression\s*\(/i,
      /vbscript:/i,
      /@import/i,
      /binding\s*:/i
    ]
    
    return xssPatterns.some(pattern => pattern.test(input))
  }
  
  // 批量处理对象属性
  static sanitizeObject<T extends Record<string, any>>(
    obj: T,
    rules: Record<keyof T, 'html' | 'text' | 'url' | 'skip'>
  ): T {
    const sanitized = { ...obj }
    
    for (const [key, rule] of Object.entries(rules)) {
      const value = sanitized[key]
      
      if (value && typeof value === 'string') {
        switch (rule) {
          case 'html':
            sanitized[key] = this.sanitizeHTML(value)
            break
          case 'text':
            sanitized[key] = this.escapeHTML(value)
            break
          case 'url':
            sanitized[key] = this.sanitizeURL(value)
            break
          case 'skip':
            // 跳过处理
            break
        }
      }
    }
    
    return sanitized
  }
}

8.3.3 请求限制与防护

实现智能的请求限制和攻击防护机制,保护应用免受各种网络攻击。

高级请求限制系统

// server/utils/rate-limiter.ts
import { LRUCache } from 'lru-cache'

interface RateLimitRule {
  windowMs: number    // 时间窗口(毫秒)
  maxRequests: number // 最大请求数
  message?: string    // 超限消息
  skipSuccessfulRequests?: boolean // 是否跳过成功请求
  keyGenerator?: (event: any) => string // 自定义键生成器
}

export class AdvancedRateLimiter {
  private cache: LRUCache<string, number[]>
  private rules: Map<string, RateLimitRule>
  
  constructor() {
    this.cache = new LRUCache({
      max: 10000,
      ttl: 1000 * 60 * 60 // 1小时
    })
    this.rules = new Map()
  }
  
  // 添加限制规则
  addRule(pattern: string, rule: RateLimitRule) {
    this.rules.set(pattern, rule)
  }
  
  // 检查请求是否被限制
  async checkLimit(event: any): Promise<{
    allowed: boolean
    remaining: number
    resetTime: number
    totalRequests: number
  }> {
    const url = getRequestURL(event)
    const rule = this.findMatchingRule(url.pathname)
    
    if (!rule) {
      return {
        allowed: true,
        remaining: -1,
        resetTime: -1,
        totalRequests: 0
      }
    }
    
    const key = rule.keyGenerator ? 
      rule.keyGenerator(event) : 
      this.generateDefaultKey(event)
    
    const now = Date.now()
    const windowStart = now - rule.windowMs
    
    // 获取时间窗口内的请求记录
    let requests = this.cache.get(key) || []
    
    // 清理过期记录
    requests = requests.filter(timestamp => timestamp > windowStart)
    
    const currentRequests = requests.length
    
    if (currentRequests >= rule.maxRequests) {
      // 计算重置时间
      const oldestRequest = Math.min(...requests)
      const resetTime = oldestRequest + rule.windowMs
      
      return {
        allowed: false,
        remaining: 0,
        resetTime,
        totalRequests: currentRequests
      }
    }
    
    // 记录当前请求
    requests.push(now)
    this.cache.set(key, requests)
    
    return {
      allowed: true,
      remaining: rule.maxRequests - requests.length,
      resetTime: now + rule.windowMs,
      totalRequests: requests.length
    }
  }
  
  private findMatchingRule(pathname: string): RateLimitRule | null {
    for (const [pattern, rule] of this.rules) {
      if (this.matchPattern(pattern, pathname)) {
        return rule
      }
    }
    return null
  }
  
  private matchPattern(pattern: string, pathname: string): boolean {
    // 支持通配符匹配
    const regex = new RegExp(
      pattern.replace(/\*/g, '.*').replace(/\?/g, '.')
    )
    return regex.test(pathname)
  }
  
  private generateDefaultKey(event: any): string {
    const ip = getClientIP(event)
    const url = getRequestURL(event)
    const method = getMethod(event)
    
    return `${ip}:${method}:${url.pathname}`
  }
}

// 全局限制器实例
const rateLimiter = new AdvancedRateLimiter()

// 配置限制规则
rateLimiter.addRule('/api/auth/login', {
  windowMs: 15 * 60 * 1000, // 15分钟
  maxRequests: 5, // 最多5次登录尝试
  message: '登录尝试过于频繁,请稍后再试'
})

rateLimiter.addRule('/api/*', {
  windowMs: 60 * 1000, // 1分钟
  maxRequests: 100, // 通用 API 限制
  keyGenerator: (event) => {
    // 认证用户使用用户ID,未认证使用IP
    const user = event.context.user
    const ip = getClientIP(event)
    return user ? `user:${user.id}` : `ip:${ip}`
  }
})

export { rateLimiter }

总结与最佳实践

安全开发生命周期

  1. 设计阶段
    • 威胁建模和安全需求分析
    • 最小权限原则设计
    • 纵深防御架构规划
  2. 开发阶段
    • 安全编码规范遵循
    • 输入验证和输出编码
    • 安全组件复用
  3. 测试阶段
    • 安全测试用例设计
    • 渗透测试和漏洞扫描
    • 安全代码审查
  4. 部署阶段
    • 安全配置管理
    • 监控和告警设置
    • 应急响应准备

监控与审计

// composables/useSecurityMonitoring.ts
export const useSecurityMonitoring = () => {
  // 安全事件监控
  const monitorSecurityEvent = async (event: {
    type: string
    severity: 'low' | 'medium' | 'high' | 'critical'
    details: any
    user?: any
  }) => {
    // 实时告警
    if (event.severity === 'critical') {
      await sendImmediateAlert(event)
    }
    
    // 记录到安全日志
    await logSecurityEvent(event)
    
    // 更新安全仪表板
    await updateSecurityDashboard(event)
  }
  
  // 审计日志记录
  const auditLog = async (action: {
    user: string
    action: string
    resource: string
    result: 'success' | 'failure'
    details?: any
  }) => {
    await $fetch('/api/audit/log', {
      method: 'POST',
      body: {
        ...action,
        timestamp: new Date().toISOString(),
        ip: await getClientIP(),
        userAgent: navigator.userAgent
      }
    })
  }
  
  return {
    monitorSecurityEvent,
    auditLog
  }
}

通过本指南的学习,开发者可以掌握 Nuxt 框架中完整的中间件和权限控制体系,构建安全、可靠的企业级应用。安全是一个持续的过程,需要在整个开发生命周期中贯彻安全意识,持续监控和改进安全措施。