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 方法中抛出的错误会显示在客户端,可以根据需求自定义错误提示。

相关推荐
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
沙蒿同学3 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
paopaokaka_luck3 天前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
Hilaku3 天前
为什么死磕 1px 的团队,用户体验反而更差?
前端·javascript·程序员
陈拉斯3 天前
给公司年会写了个大屏抽奖系统:动画在前端跑,凭什么说结果没被改?
javascript
yzy853 天前
Vue中下载或打开文件的JavaScript方法
前端·javascript·vue
用户298698530143 天前
在 React 中用 JavaScript 将 Word 转换为文本
前端·javascript·react.js
李少兄3 天前
深入解析 JavaScript 中的 `document` 对象
javascript