next-auth v5 结合 Prisma 实现登录与会话管理

1. 安装依赖

复制代码
npm install next-auth prisma @prisma/client

2. 配置 Prisma 模型

在 prisma/schema.prisma 文件中定义 User 和 Account 模型(next-auth v5 推荐使用自定义模型)。

javascript 复制代码
model User {
  id            String    @id @default(uuid())
  email         String    @unique
  name          String?
  image         String?
  hashedPassword String?
  accounts      Account[]
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
}

model Account {
  id               String  @id @default(uuid())
  userId           String
  provider         String
  providerAccountId String
  user             User    @relation(fields: [userId], references: [id], onDelete: Cascade)
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
}

运行 prisma migrate 同步数据库:

复制代码
npx prisma migrate dev --name init

3. 创建 next-auth 配置

pages/api/auth/[...nextauth].ts 文件中添加 next-auth 配置:

javascript 复制代码
import NextAuth, { AuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaClient } from "@prisma/client";
import { compare } from "bcryptjs"; // 假设密码是加密存储的

const prisma = new PrismaClient();

export const authOptions: AuthOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials.password) {
          throw new Error("Missing email or password");
        }

        // 查找用户
        const user = await prisma.user.findUnique({
          where: { email: credentials.email },
        });

        if (!user) {
          throw new Error("No user found");
        }

        // 验证密码
        const isValidPassword = await compare(
          credentials.password,
          user.hashedPassword
        );

        if (!isValidPassword) {
          throw new Error("Invalid credentials");
        }

        return { id: user.id, email: user.email, name: user.name };
      },
    }),
  ],
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user.id = token.id;
      }
      return session;
    },
  },
};

export default NextAuth(authOptions);`

4. 创建登录页面

示例的登录页面:

javascript 复制代码
import { signIn } from "next-auth/react";
import { useState } from "react";

export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const result = await signIn("credentials", {
      email,
      password,
      redirect: false,
    });

    if (result?.error) {
      setError(result.error);
    } else {
      // 登录成功,跳转
      window.location.href = "/";
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Email</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>
      <div>
        <label>Password</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>
      {error && <p style={{ color: "red" }}>{error}</p>}
      <button type="submit">Login</button>
    </form>
  );
}

5. 使用会话信息

在需要使用用户会话的页面中:

javascript 复制代码
import { useSession, signOut } from "next-auth/react";

export default function Dashboard() {
  const { data: session, status } = useSession();

  if (status === "loading") {
    return <p>Loading...</p>;
  }

  if (!session) {
    return <p>You are not logged in</p>;
  }

  return (
    <div>
      <h1>Welcome, {session.user.name}</h1>
      <button onClick={() => signOut()}>Sign out</button>
    </div>
  );
}

6. 运行应用

复制代码
npm run dev

关键点说明

密码存储:建议在用户注册时使用 bcrypt 对密码进行加密存储。

Session 策略:此示例使用 JWT 策略,如果需要使用数据库会话,可以将 session.strategy 设置为 "database" 并配置 Prisma 的 Session 模型。

错误处理:authorize 方法中抛出的错误会显示在客户端,可以根据需求自定义错误提示。

相关推荐
老前端的功夫5 小时前
Vue 3 性能深度解析:从架构革新到运行时的全面优化
javascript·vue.js·架构
前端 贾公子6 小时前
vue移动端适配方案 === postcss-px-to-viewport
前端·javascript·html
GISer_Jing7 小时前
AI营销增长:4大核心能力+前端落地指南
前端·javascript·人工智能
m0_471199638 小时前
【场景】前端怎么解决离线收银、数据同步异常等场景问题
前端·javascript
栀秋6669 小时前
“无重复字符的最长子串”:从O(n²)哈希优化到滑动窗口封神,再到DP降维打击!
前端·javascript·算法
xhxxx9 小时前
不用 Set,只用两个布尔值:如何用标志位将矩阵置零的空间复杂度压到 O(1)
javascript·算法·面试
有意义9 小时前
斐波那契数列:从递归到优化的完整指南
javascript·算法·面试
Mr.Jessy9 小时前
JavaScript高级:深入对象与内置构造函数
开发语言·前端·javascript·ecmascript
温宇飞9 小时前
深入理解 JavaScript 模块系统:CJS 与 ESM 的实现原理
javascript