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

如何在NextAuth中分离管理员与用户的认证API路由及会话

解决NextAuth中管理员与普通用户的认证路由分离与会话管理问题

需求背景

基于Next.js搭建的全栈电商系统,包含管理员、普通用户两类角色,数据分别存储在独立MySQL数据表中。使用NextAuth实现身份认证,需要分离两类用户的认证API路由:管理员对应/api/admin(由NextAuth路由处理登录逻辑)、普通用户对应/api/user,同时解决当前管理员认证的错误问题。

解决方案步骤

1. 创建分离的NextAuth API路由

在pages/api目录下分别创建两类用户的NextAuth路由文件:

  • 管理员路由:pages/api/admin/[...nextauth].js
  • 普通用户路由:pages/api/user/[...nextauth].js

修改管理员路由配置(添加角色标识与会话回调)

import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
import Admin from "../../../../models/admin";
import { verifyPassword } from "../../../../utils/hash-p";

export default NextAuth({
  secret: process.env.NEXTAUTH_SECRET, // 必须配置,从环境变量读取
  session: {
    jwt: true,
  },
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        try {
          const admin = await Admin.findOne({
            where: { email: credentials.email },
          });

          if (!admin) throw new Error("管理员不存在");

          const isValid = await verifyPassword(
            credentials.password,
            admin.password
          );

          if (!isValid) throw new Error("密码错误");

          // 返回包含角色的用户信息,用于后续会话标识
          return { email: admin.email, role: "admin" };
        } catch (error) {
          throw new Error(error.message);
        }
      },
    }),
  ],
  // 添加回调函数,将角色信息存入JWT和会话
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.role = user.role;
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.role = token.role;
      }
      return session;
    },
  },
  // 自定义登录页面跳转
  pages: {
    signIn: "/admin/login",
  },
});

普通用户路由示例(类似结构)

import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
import User from "../../../../models/user";
import { verifyPassword } from "../../../../utils/hash-p";

export default NextAuth({
  secret: process.env.NEXTAUTH_SECRET,
  session: {
    jwt: true,
  },
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        try {
          const user = await User.findOne({
            where: { email: credentials.email },
          });

          if (!user) throw new Error("用户不存在");

          const isValid = await verifyPassword(
            credentials.password,
            user.password
          );

          if (!isValid) throw new Error("密码错误");

          return { email: user.email, role: "user" };
        } catch (error) {
          throw new Error(error.message);
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) token.role = user.role;
      return token;
    },
    async session({ session, token }) {
      if (session.user) session.user.role = token.role;
      return session;
    },
  },
  pages: {
    signIn: "/user/login",
  },
});

2. 客户端登录调用指定路由

修改管理员登录页面的signIn调用,指定使用管理员专属的NextAuth路由:

import { useRef } from "react";
import Image from "next/image";
import { signIn } from "next-auth/react";

const AdminLogin = () => {
  const handleRequest = async (e) => {
    e.preventDefault();
    const email = emailRef.current.value;
    const password = passRef.current.value;

    const result = await signIn("credentials", {
      redirect: false,
      email,
      password,
      auth: "/api/admin", // 指定管理员路由前缀
      callbackUrl: "/admin/dashboard", // 登录成功后跳转页面
    });

    console.log(result);
    // 处理登录错误提示
    if (result.error) {
      alert(result.error);
    }
  };

  const emailRef = useRef();
  const passRef = useRef();

  return (
    <main className="form-signin w-100 m-auto">
      <div
        className="row justify-content-center align-items-center"
        style={{ height: "100vh" }}
      >
        <div className="col-sm-3 col-11">
          <form onSubmit={handleRequest}>
            <div
              className="position-relative text-center"
              style={{ height: "150px" }}
            >
              <Image
                className="mb-4"
                src="/assets/images/logo.png"
                width={100}
                height={143}
                alt="benizh"
                unoptimized
              />
            </div>
            <h1 className="h3 mb-3 fw-normal text-center">ورود ادمین</h1>

            <div className="form-floating mb-3">
              <input
                type="email"
                className="form-control text-end"
                id="floatingInput"
                placeholder="name@example.com"
                style={{ direction: "ltr" }}
                ref={emailRef}
                required
              />
              <label htmlFor="floatingInput">پست الکترونیک</label>
            </div>
            <div className="form-floating mb-3">
              <input
                type="password"
                className="form-control text-end"
                id="floatingPassword"
                placeholder="Password"
                style={{ direction: "ltr" }}
                ref={passRef}
                required
              />
              <label htmlFor="floatingPassword">رمز عبور</label>
            </div>

            <button className="w-100 btn btn-lg btn-primary" type="submit">
              ورود
            </button>
          </form>
        </div>
      </div>
    </main>
  );
};

export default AdminLogin;

3. 会话权限校验

在需要权限的页面或API路由中,通过会话信息区分角色:

// 前端页面中使用useSession获取角色
import { useSession } from "next-auth/react";

const AdminDashboard = () => {
  const { data: session, status } = useSession();

  if (status === "loading") return <div>加载中...</div>;
  if (!session || session.user.role !== "admin") {
    return <div>无权访问此页面</div>;
  }

  return <div>管理员仪表盘</div>;
};

export default AdminDashboard;

4. 常见错误排查

  • 环境变量缺失:确保.env文件中配置了NEXTAUTH_SECRET(可通过openssl rand -hex 32生成)
  • 数据库查询失败:检查Sequelize模型定义、数据库连接字符串是否正确,确保Admin表存在且字段匹配
  • 密码验证逻辑错误:确认verifyPassword函数正确使用哈希对比(如bcrypt的compare方法)
  • 路由路径错误:确保signIn的auth参数与NextAuth路由文件路径一致(如管理员路由是/api/admin/[...nextauth].js,则auth值为/api/admin)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 23:03:15