我想获取中间件中任何请求的登录状态,并且我想将其重定向到受保护的路由或登录路由。
但我没有在
middleware.ts
内进行会话。我使用了这段代码,但仍然显示错误。
//auth.ts
import NextAuth from "next-auth"
export const { auth, handlers } = NextAuth({
callbacks: {
authorized: async ({ auth }) => {
// Logged in users are authenticated, otherwise redirect to login page
return !!auth
},
},
})
// middleware.ts
import { auth } from "@/auth"
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname !== "/login") {
const newUrl = new URL("/login", req.nextUrl.origin)
return Response.redirect(newUrl)
}
})
对于初学者,请迁移到 AuthJS v5。其次,问题似乎不是来自下一个身份验证,而是您没有安装 aws-sdk,这就是错误的原因。
假设您正在实施 RBAC(基于角色的访问控制),这个中间件可以为您工作:
//middleware.ts
// Importing necessary functions from the "next/server" module
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// Importing the auth function from our auth.ts file
import { auth } from "@/auth";
// Defining the protected routes with the roles allowed to access them, including dynamic segments
const protectedRoutes = [
{ path: "/admin/:path", roles: ["ADMIN"] }, // Only ADMIN can access this route
{ path: "/user/:path", roles: ["ADMIN", "USER"] }, // Both ADMIN and USER can access this route
];
// Function to find the current route in the protectedRoutes array
const findCurrentRoute = (pathname: string) => {
return protectedRoutes.find((route) =>
new RegExp(`^${route.path.replace(/:\w+/g, "\\w+")}$`).test(pathname)
);
};
// Middleware function to handle requests
export default async function middleware(request: NextRequest) {
// Get the session using the auth function
const session = await auth();
// Find the current route in the protectedRoutes array
const currentRoute = findCurrentRoute(request.nextUrl.pathname);
// Check if the request path is protected
if (currentRoute) {
// If the route is protected and there's no session, redirect to the login page
if (!session) {
const absoluteURL = new URL("/auth/login", request.nextUrl.origin);
return NextResponse.redirect(absoluteURL.toString());
}
// If the user role is not allowed for the current route, redirect to the unauthorized page
if (!currentRoute.roles.includes(session.user.role)) {
const absoluteURL = new URL("/unauthorized", request.nextUrl.origin);
return NextResponse.redirect(absoluteURL.toString());
}
}
// If the route is not protected or the user has access, continue to the next middleware or the requested page
return NextResponse.next();
}
// Configuration to match all paths except for certain static files and API routes
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
此外,这里还有一个初学者级别配置的 auth.config.ts 和 auth.ts,具有 Github、Google 和基于凭据的身份验证。如果您要在 vercel 上部署,则兼容边缘情况。
//auth.config.ts
import Github from "next-auth/providers/github";
import Google from "next-auth/providers/google";
import { type NextAuthConfig } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import bcrypt from "bcrypt-edge";
import { prisma } from "./prisma";
// Adding the callbacks for managing the session and token
export default {
providers: [
Github({
clientId: process.env.AUTH_GITHUB_ID,
clientSecret: process.env.AUTH_GITHUB_SECRET,
allowDangerousEmailAccountLinking: true
}),
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
allowDangerousEmailAccountLinking: true
}),
Credentials({
name: "Credentials",
credentials: {
email: {
label: "Email",
type: "email",
placeholder: "[email protected]",
},
password: { label: "Password", type: "password" },
},
authorize: async (credentials) => {
if (!credentials || !credentials.email || !credentials.password) {
return null;
}
const email = credentials.email as string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const user: any = await prisma.user.findUnique({
where: {
email,
},
});
if (!user) {
throw new Error("No account found with this email.");
} else {
const isMatch = bcrypt.compareSync(
credentials.password as string,
user.hashedPassword
);
if (!isMatch) {
throw new Error("Incorrect password.");
}
}
// Return user object with role information
return { ...user, role: user.role };
},
}),
],
callbacks: {
// Callback to manage the JWT token
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
// Callback to manage the session object
session: async ({ session, token }) => {
if (token) {
session.user.id = token.id as string;
session.user.role = token.role as string;
}
return session;
},
},
// Additional configuration can go here (e.g., pages)
pages: {
signIn: "/sign-in",
},
} satisfies NextAuthConfig;
//auth.ts
import NextAuth from "next-auth";
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "./prisma"
import authConfig from "./auth.config";
export const {
handlers: { GET, POST },
signIn,
signOut,
auth,
} = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: "jwt" },
...authConfig,
});
以及使用 bcrypt (bcrypt-edge) 进行加盐和散列的代码:
import bcrypt from "bcrypt-edge";
export function saltAndHashPassword(password: any) {
const saltRounds = 10; // Adjust the cost factor according to your security requirements
const salt = bcrypt.genSaltSync(saltRounds); // Synchronously generate a salt
const hash = bcrypt.hashSync(password, salt); // Synchronously hash the password
return hash; // Return the hash directly as a string
}
您可能会在 auth.config.ts 中收到 TypeScript 错误 RBAC,修复方法如下:
import { DefaultSession, DefaultUser } from "@auth";
// Extend the User interface to include role
declare module "next-auth" {
interface User extends DefaultUser {
role?: string;
}
interface Session {
user?: {
id?: string;
role?: string;
} & DefaultSession["USER"];
}
}
确保您的用户确实具有角色字段。示例 schema.prisma:
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
email String? @unique
hashedPassword String?
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
role UserRole? @default(USER)
Authenticator Authenticator[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
blogPosts BlogPost[]
}
此外,您还必须确保您的数据库具有这些工作所需的架构,您可以在此处获取这些(选择数据库的连接方法):AuthJS v5 数据库适配器