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

Next-Auth authconfig扩展:基于isAdmin的路由跳转与权限校验

解决Next-Auth用户信息不全及登录后角色跳转问题

1. 补全用户信息存储逻辑

要让auth对象能获取到完整用户字段,核心是在JWT和Session流程中携带自定义字段:

修改authconfig.ts的CredentialsProvider配置

在authorize回调中返回包含所有字段的用户对象,再通过jwt和session回调将字段同步到token和session中:

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

// 模拟数据库查询用户的方法,替换成你的实际逻辑
const fetchUserByEmail = async (email: string | undefined) => {
  // 示例数据,实际从数据库读取
  return email === "admin@example.com" 
    ? { id: "1", email: "admin@example.com", username: "admin", img: "/admin.jpg", isAdmin: true, password: "123456" }
    : { id: "2", email: "user@example.com", username: "user", img: "/user.jpg", isAdmin: false, password: "123456" };
};

export const authOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "邮箱", type: "email" },
        password: { label: "密码", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) return null;
        
        const user = await fetchUserByEmail(credentials.email);
        if (!user) return null;

        // 实际项目请用bcrypt等加密库验证密码,这里仅做示例
        const isPasswordValid = credentials.password === user.password;
        if (!isPasswordValid) return null;

        // 返回完整用户对象,包含需要的所有字段
        return {
          id: user.id,
          email: user.email,
          username: user.username,
          img: user.img,
          isAdmin: user.isAdmin
        };
      }
    })
  ],
  // CredentialsProvider推荐用JWT存储会话
  session: { strategy: "jwt" },
  callbacks: {
    // 将自定义字段写入JWT token
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.username = user.username;
        token.img = user.img;
        token.isAdmin = user.isAdmin;
      }
      return token;
    },
    // 将token中的字段映射到session.user,让前端/auth对象能读取到
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.id as string;
        session.user.username = token.username as string;
        session.user.img = token.img as string;
        session.user.isAdmin = token.isAdmin as boolean;
      }
      return session;
    }
  }
};

export default NextAuth(authOptions);

2. 基于isAdmin做权限校验

现在auth.user已包含isAdmin字段,可在authorized回调中实现路由权限控制:

// 在authOptions的callbacks中添加authorized回调
async authorized({ auth, request }) {
  const isLoggedIn = !!auth?.user;
  const targetPath = request.nextUrl.pathname;

  // 管理员仅能访问dashboard相关路由
  if (targetPath.startsWith("/dashboard")) {
    return auth?.user?.isAdmin === true;
  }
  // 普通用户仅能访问store相关路由
  if (targetPath.startsWith("/store")) {
    return isLoggedIn && auth?.user?.isAdmin === false;
  }
  // 未登录用户跳转登录页
  return isLoggedIn;
}

3. 登录后按角色跳转

有两种常用实现方式,按需选择:

方式1:前端登录组件手动控制跳转

在登录页面调用signIn时关闭自动跳转,拿到会话信息后手动跳转:

import { signIn, getSession } from "next-auth/react";

const handleLogin = async (e: React.FormEvent) => {
  e.preventDefault();
  const email = (e.target as any).email.value;
  const password = (e.target as any).password.value;

  const result = await signIn("credentials", {
    email,
    password,
    redirect: false // 关闭自动跳转
  });

  if (result?.ok) {
    const session = await getSession();
    if (session?.user?.isAdmin) {
      window.location.href = "/dashboard";
    } else {
      window.location.href = "/store";
    }
  }
};

方式2:用Middleware统一处理跳转

在middleware.ts中拦截已登录用户的请求,自动跳转到对应角色页面:

import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";

export default withAuth(
  function middleware(req) {
    const isAdmin = req.nextauth.token?.isAdmin;
    const currentPath = req.nextUrl.pathname;

    // 管理员不在dashboard页面则跳转
    if (isAdmin && !currentPath.startsWith("/dashboard")) {
      return NextResponse.redirect(new URL("/dashboard", req.url));
    }
    // 普通用户不在store页面则跳转
    if (!isAdmin && !currentPath.startsWith("/store")) {
      return NextResponse.redirect(new URL("/store", req.url));
    }
  },
  {
    callbacks: {
      authorized: ({ token }) => !!token // 仅拦截已登录用户
    }
  }
);

// 指定需要拦截的路由
export const config = {
  matcher: ["/", "/dashboard/:path*", "/store/:path*"]
};

核心注意事项

  • 必须设置session.strategy: "jwt",CredentialsProvider默认不支持数据库会话,只有JWT能携带自定义字段。
  • jwt和session回调的字段映射必须一一对应,否则auth.user无法获取到非默认字段。
  • 权限校验和跳转逻辑可结合使用,比如用authorized控制路由访问权限,用middleware处理自动跳转。

内容的提问来源于stack exchange,提问作者Tejendra Singh Rajawat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 07:42:29