# Next.js应用中的授权机制

> Site index: <https://blog.suxiong.me/llms.txt>

Original: [Next.js应用中的授权机制](https://blog.suxiong.me/blog/authorization-in-nextjs)

Author: Suxiong

Date: 2025-04-19

深入探讨Next.js应用中的授权机制实现方案。文章从数据访问授权、路由授权、用户界面授权和中间件授权等多个维度，详细介绍了如何在Next.js应用中实现安全可靠的授权机制。同时提供了具体的代码示例和最佳实践建议。

Tags: Next.js / 授权机制 / 安全 / 前端开发

在Next.js应用的开发进程中，搭建授权机制是筑牢应用安全防线，保障用户数据安全，确保用户对功能访问合规的核心任务。此前，Vercel CEO与Cloudflare CEO曾就一起[安全事件](https://nextjs.org/blog/cve-2025-29927)公开[互怼](https://www.oschina.net/news/340676)，这一事件也促使相关方反思和改进安全策略。本文不仅围绕此次事件展开探讨，同时也参考了Next.js开发团队所推荐的[安全实现方案](https://nextjs.org/blog/security-nextjs-server-components-actions) ，旨在为开发者提供多维度的安全指引，助力打造更安全、可靠的Next.js应用。

## 一、数据访问授权

在全栈应用开发中，数据访问授权用以控制对数据的读取和写入操作，这一过程通常在API层完成。

### 1. 小型应用的实现

对于规模较小的应用，可在数据获取函数或服务器操作内进行授权检查。以获取文章列表`getPosts`和创建文章`createPost`为例：

```javascript
import { getAuth } from './auth';

async function getPosts() {
    const session = await getAuth();
    if (!session) {
        throw new Error('未授权');
    }
    // 获取文章数据的逻辑
}

async function createPost(post) {
    const session = await getAuth();
    if (!session) {
        throw new Error('未授权');
    }
    // 创建文章的逻辑
}
```

getAuth函数针对数据库执行会话 Cookie 的验证。值得注意的是，我们在这里使用React 的[cache API](https://react.dev/reference/react/cache)，以便在一个渲染周期内记忆化getAuth函数的结果：

```javascript
export const getAuth = cache(async () => {
  const sessionToken =
    (await cookies()).get("session")?.value ?? null;

  if (!sessionToken) {
    return {
      user: null,
      session: null,
    };
  }

  // "expensive" database call
  return await validateSession(sessionToken);
});
```

### 2. 大型应用的分层授权

当应用规模增大，引入服务层和数据访问层后，授权过程可分层进行。API层验证用户身份，服务层执行核心授权逻辑，数据访问层作为最终的数据保护屏障。例如，在一个包含多层架构的博客应用中：

```javascript
// API层示例
import { getAuth } from './auth';
import { getPostsService } from './services/postService';

async function apiGetPosts(req, res) {
    const session = await getAuth();
    if (!session) {
        return res.status(401).json({ error: '未授权' });
    }
    try {
        const posts = await getPostsService();
        return res.status(200).json(posts);
    } catch (error) {
        return res.status(500).json({ error: '服务器错误' });
    }
}

// 服务层示例
async function getPostsService() {
    // 在此添加业务规则和权限检查
    return await getPostsData();
}

// 数据访问层示例
async function getPostsData() {
    // 实际查询数据库获取文章数据的逻辑
}
```

## 二、路由授权

### 1. 页面组件内授权

在Next.js应用中，可在页面组件内进行授权检查，阻止未授权用户访问特定路由。以`PostsPage`组件为例：

```javascript
import { getAuth } from './auth';

async function PostsPage() {
    const session = await getAuth();
    if (!session) {
        return <Redirect to="/login" />;
    }
    // 页面内容
}
```

### 2. 布局组件授权

为了简化授权逻辑，可将授权检查移至布局组件，如`AuthLayout`：

```javascript
import { getAuth } from './auth';

async function AuthLayout({ children }) {
    const session = await getAuth();
    if (!session) {
        return <Redirect to="/login" />;
    }
    return <>{children}</>;
}
```

然而，在布局组件中进行授权存在安全风险，因为页面组件可能会独立被获取。为应对这一问题，可采用以下策略：

- **后端独立检查**：将布局检查作为开发便利性的提升手段，在后端层进行严格的独立授权检查。
- **页面组件检查**：在页面组件内进行授权检查，确保安全性，牺牲一定的开发便利性。
- **两者结合**：结合布局组件和页面组件的授权检查，平衡安全性和开发体验。

### 3. 角色和权限管理

当应用涉及不同角色和权限时，可设置多个布局组件，如`AdminLayout`：

```javascript
import { getAuth } from './auth';

async function AdminLayout({ children }) {
    const session = await getAuth();
    if (!session ||!session.user.isAdmin) {
        return <Redirect to="/" />;
    }
    return <>{children}</>;
}
```

## 三、用户界面授权

UI授权用于提升用户体验，根据用户授权状态隐藏或禁用特定UI元素。以`PostMoreMenu`组件为例：

```javascript
import { getAuth } from './auth';

function PostMoreMenu() {
    const { user } = getAuth();
    return (
        <div>
            <button>分享</button>
            {user && <button>删除</button>}
        </div>
    );
}
```

需注意，UI授权并非安全机制，恶意用户可操纵客户端代码绕过限制，核心授权检查仍需在API、服务和数据访问层进行。

## 四、中间件授权

在Next.js中，可使用中间件保护页面和路由免受未授权访问：

```javascript
import { NextResponse } from 'next/server';
import { getAuth } from './auth';

export async function middleware(req) {
    const session = await getAuth();
    if (!session) {
        return NextResponse.redirect(new URL('/login', req.url));
    }
    return NextResponse.next();
}
```

但中间件在每个请求时都会执行，若进行数据库查询会影响性能。因此，建议进行预检检查，如检查会话cookie是否存在。

你也可以在中间件中[刷新 Cookie 的过期时间](https://lucia-auth.com/sessions/cookies/nextjs)，这是一种常见的做法，可以让用户在更长时间内保持登录状态。

此外，中间件运行在边缘运行时环境，与Node.js环境不同，存在一定限制。不过Next团队也在致力于切换为[在 Node.js 中运行中间件](https://nextjs.org/blog/next-15-2#nodejs-middleware-experimental)。

而且Next.js应用仅支持一个中间件文件，这对复杂应用的细粒度控制提出了挑战。不过Next团队正在致力于一个名为[Interceptors](https://github.com/vercel/next.js/pull/70961)的解决方案。
