You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js中安全保护静态站点生成(SSG)路由?

安全保护Next.js SSG页面的可行方案

你提到的客户端重定向确实有致命的安全漏洞——用户只要篡改本地状态就能绕开校验,直接看到预先生成的静态内容。完全没必要为了权限要求把所有页面改成SSR,这里有几个更安全的服务端层面解决方案:

1. 用Next.js Middleware(首推方案)

Next.js 12及以上版本提供的Middleware是处理这类权限校验的最佳实践,它会在请求到达页面之前,就在边缘层(Edge Runtime)执行拦截逻辑,完全绕开客户端操作,从根源上杜绝篡改本地状态的可能。

举个结合Auth0的实战例子,在项目根目录创建middleware.ts文件:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import jwt from 'jsonwebtoken'

export function middleware(request: NextRequest) {
  // 从请求Cookie中获取Auth0签发的JWT令牌
  const token = request.cookies.get('auth0-token')?.value

  // 验证令牌的有效性(签名、过期时间等)
  const isAuthenticated = validateToken(token)

  // 拦截未认证用户访问受保护的SSG页面
  if (!isAuthenticated && request.nextUrl.pathname.startsWith('/protected')) {
    const loginUrl = new URL('/login', request.url)
    // 保存原访问路径,登录后可自动跳转回来
    loginUrl.searchParams.set('returnTo', request.nextUrl.pathname)
    return NextResponse.redirect(loginUrl)
  }

  // 认证通过,放行请求
  return NextResponse.next()
}

// 配置需要触发中间件的路径规则
export const config = {
  matcher: '/protected/:path*',
}

// 实现实际的令牌验证逻辑
function validateToken(token: string | undefined): boolean {
  if (!token) return false
  try {
    // 替换成你的Auth0密钥和验证逻辑
    jwt.verify(token, process.env.AUTH0_SECRET_KEY!, {
      audience: process.env.AUTH0_AUDIENCE,
      issuer: process.env.AUTH0_ISSUER,
    })
    return true
  } catch (err) {
    return false
  }
}

这个方案的核心优势:

  • 拦截逻辑在服务端/边缘节点执行,客户端根本碰不到受保护的静态内容
  • 统一配置路径规则,不用在每个页面重复写校验代码
  • 基于Edge Runtime,性能开销极小,完全保留SSG的性能优势

2. 边缘函数(Edge Functions)

如果你的部署平台支持边缘函数(比如Vercel、Cloudflare Pages),可以直接用它实现类似Middleware的拦截逻辑。本质上Next.js Middleware就是基于Edge Runtime的,如果你需要和平台特定功能集成,边缘函数会更灵活。

比如在Cloudflare Pages中,创建functions/protected/[[path]].ts文件,处理所有/protected路径的请求:

import jwt from 'jsonwebtoken'

export async function onRequest(context) {
  const { request, env } = context
  const token = request.cookies.get('auth0-token')?.value

  // 验证令牌
  if (!token || !validateToken(token, env)) {
    return Response.redirect(new URL('/login', request.url))
  }

  // 认证通过,返回静态内容
  return await env.ASSETS.fetch(request)
}

function validateToken(token: string, env: any): boolean {
  try {
    jwt.verify(token, env.AUTH0_SECRET_KEY, {
      audience: env.AUTH0_AUDIENCE,
      issuer: env.AUTH0_ISSUER,
    })
    return true
  } catch (err) {
    return false
  }
}

3. 自定义服务器(不推荐)

如果你还在使用Next.js旧版本,或者有特殊需求,可以用自定义服务器(比如Express)处理权限校验,但这个方案会失去Next.js的自动优化能力(比如自动静态优化、增量静态再生),所以除非必要不建议使用。

示例Express服务器代码:

const express = require('express')
const next = require('next')
const jwt = require('jsonwebtoken')
const cookieParser = require('cookie-parser')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()
  server.use(cookieParser())

  // 给受保护路径添加权限校验中间件
  server.use('/protected/*', (req, res, next) => {
    const token = req.cookies['auth0-token']
    if (!token) {
      return res.redirect('/login')
    }
    try {
      jwt.verify(token, process.env.AUTH0_SECRET_KEY, {
        audience: process.env.AUTH0_AUDIENCE,
        issuer: process.env.AUTH0_ISSUER,
      })
      next()
    } catch (err) {
      return res.redirect('/login')
    }
  })

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

额外提醒

如果你的受保护页面包含用户专属的动态内容(比如个人资料页、定制化仪表盘),那SSG确实不太合适——因为静态页面是构建时生成的,没法针对每个用户动态渲染内容。这种情况下,你可以考虑:

  • 用getStaticProps结合增量静态再生(ISR),但仍然无法处理用户专属内容
  • 改成SSR(getServerSideProps),在服务端获取用户信息并渲染专属内容

但如果只是页面需要权限访问,内容是通用的(比如内部文档、后台框架页),上面的Middleware/边缘函数方案完全可以满足需求,既能保留SSG的性能优势,又能实现安全的权限控制。

内容的提问来源于stack exchange,提问作者Thor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:15:29