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

Next.js 14中如何用Next Auth正确保护Server Actions?

Next.js 14中如何用Next Auth正确保护Server Actions?

嘿,这个问题我刚上手Server Actions的时候也踩过一模一样的坑!确实,Next Auth的中间件只负责拦截页面路由和API路由的请求,但Server Actions是通过Next.js内部的RPC机制调用的,中间件不会直接管这些请求,所以才会出现你说的未登录用户也能调用敏感查询的情况。不过解决起来思路很明确——必须在Server Actions内部做服务器端的会话验证,而且可以通过封装来避免重复代码,下面给你具体的实现方案:

方案一:在核心数据库操作函数中添加认证校验

既然你所有的数据库操作都通过executeQuery来执行,那最直接的方式就是在这个函数开头加上会话验证。因为Server Actions是运行在服务器端的,你可以直接用Next Auth的getServerSession来获取当前会话,而不需要依赖客户端的useSession(客户端状态是不可信的,必须在服务器端校验)。

首先确保你已经在Next Auth的配置文件(一般是app/api/auth/[...nextauth]/route.js)中导出了authOptions,然后在actions/db.js中导入它并添加校验:

import { getServerSession } from "next-auth/next";
// 导入你的Next Auth配置
import { authOptions } from "../app/api/auth/[...nextauth]/route";

export async function executeQuery(query, values = []) {
  // 第一步:验证用户是否已登录
  const session = await getServerSession(authOptions);
  if (!session) {
    // 抛出未授权错误,客户端调用时会捕获到这个错误
    throw new Error("未授权访问,请先登录");
  }

  // (可选)如果需要更细粒度的权限控制,比如只有管理员能查用户数据
  // if (session.user.role !== "admin") {
  //   throw new Error("权限不足,无法执行此操作");
  // }

  // 原有的数据库操作逻辑
  await connectDatabase();
  const connection = await pool.getConnection();
  try {
    const [results] = await connection.execute(query, values);
    return results;
  } finally {
    connection.release();
  }
}

这样一来,任何未登录的用户调用executeQuery都会直接抛出错误,没法执行数据库操作。

方案二:封装通用的认证校验函数(推荐)

如果你的Server Actions不止数据库操作,还有其他敏感逻辑,最好把认证逻辑封装成一个通用函数,避免在每个Action里重复写校验代码:

// actions/auth-utils.js
import { getServerSession } from "next-auth/next";
import { authOptions } from "../app/api/auth/[...nextauth]/route";

export async function requireAuth() {
  const session = await getServerSession(authOptions);
  if (!session) {
    throw new Error("Unauthorized");
  }
  // 返回会话信息,方便后续做权限判断
  return session;
}

然后在需要保护的Server Actions里直接调用这个函数:

// actions/db.js
import { requireAuth } from "./auth-utils";

export async function executeQuery(query, values = []) {
  // 先校验认证状态
  const session = await requireAuth();

  // (可选)权限校验
  // if (!session.user.permissions.includes("read_users")) {
  //   throw new Error("无权限读取用户数据");
  // }

  // 原数据库逻辑...
}

// 其他敏感Action示例
export async function updateUserProfile(userId, data) {
  const session = await requireAuth();
  // 确保用户只能修改自己的资料
  if (session.user.id !== userId) {
    throw new Error("无法修改他人资料");
  }
  // 执行更新操作...
}

这种方式更灵活,也更容易维护,后续要调整认证逻辑只需要改requireAuth一个地方就行。

关键注意事项

  1. 永远不要依赖客户端的状态做校验:客户端的useSession只能用来做UI展示,不能用来判断是否允许执行敏感操作——因为客户端状态可以被篡改,必须在服务器端用getServerSession做最终校验。
  2. 保持参数化查询:你现在用?占位符的方式很好,一定要坚持,这是防止SQL注入的核心手段,即使做了认证也不能放松。
  3. 区分公开和受保护的操作:如果有些数据库查询是允许未登录用户调用的(比如首页的公开内容),可以把executeQuery拆成两个函数:publicExecuteQuery(无校验)和protectedExecuteQuery(带校验),避免误保护。

关于API路由的疑问

你提到是不是要回到API路由——其实完全没必要,Server Actions的设计就是为了简化后端逻辑,只要做好服务器端的认证校验,它比API路由更方便。API路由的保护本质上也是在路由 handler 里做同样的getServerSession校验,和Server Actions的思路是一致的。

备注:内容来源于stack exchange,提问作者Benjamin Göller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 12:49:51