我的远程实习(六) | 一个demo讲清Auth.js国外平台登录鉴权👈|nextjs

前言

前些时候 ,远程实习要求实现鉴权登录 , 采用 Auth.js 可用于在 Nextjs 项目中实现登录鉴权 ,今天我写一个 demo , 为大家展示一下 auth 鉴权的数据流转 ~

我们首先介绍一下: NextAuth.js 是一个专为 Next.js 应用设计的灵活且易于使用认证库,它极大地简化了添加登录、注册、登出等认证功能的过程。该库以其灵活性和强大的功能而闻名,支持多种认证方式,包括电子邮件与密码、OAuth 2.0 提供商(如 Google、GitHub、Facebook 等),以及自定义提供商。

主要特点

  • 丰富的内置提供者:支持众多 OAuth 和 OpenID Connect 提供商,方便快捷地与第三方服务集成。
  • 会话管理:提供简明的 API 来处理用户会话,轻松获取当前用户的会话信息。
  • 数据库兼容性:支持与多种数据库集成,适用于存储用户数据,并能无缝对接无头 CMS 和自定义后端。
  • 多语言支持:根据用户的语言偏好显示错误消息及其他文本,增强用户体验。
  • 可定制页面:允许开发者创建符合应用设计风格的自定义登录、注册或错误页面。
  • 安全优先:采用一系列安全默认设置,帮助保护应用免受常见的安全威胁。
  • API 路由整合:利用 Next.js 的 API 路由机制来实现身份验证逻辑,让开发者可以自由创建用于登录、登出等操作的自定义端点。
  • 会话管理选项:提供选择,既可以通过 JSON Web Tokens (JWT) 实现无状态会话管理,也可以选择基于数据库的会话管理。
  • 适配器支持:为了满足将用户数据持久化到数据库的需求,NextAuth.js 提供了与不同数据库系统(如 Prisma、TypeORM 等)集成的适配器。

demo 效果展示

Gitee:gitee.com/luli1314520...

开源不易 , 点个小小赞支持下 ~

认证演示项目登录流程分析

项目结构

项目使用Next.js框架构建,采用App Router架构,主要目录结构:

  • /app - 前端页面和API路由
    • /app/auth/signin - 登录页面
    • /app/api/auth/[...nextauth] - NextAuth API路由
  • /auth - NextAuth配置
  • /services - 业务逻辑层
  • /models - 数据访问层
  • /types - 类型定义
  • /lib - 工具函数

认证方案

项目使用NextAuth.js实现第三方OAuth认证,支持以下登录方式:

  • Google OAuth登录
  • GitHub OAuth登录

认证数据流转图

plain 复制代码
┌─────────────┐      ┌─────────────────┐      ┌────────────────────┐
│             │      │                 │      │                    │
│  用户界面   │─────▶│  NextAuth API   │─────▶│  OAuth提供商       │
│  (客户端)   │◀─────│  (服务端路由)   │◀─────│  (Google/GitHub)   │
│             │      │                 │      │                    │
└─────────────┘      └─────────────────┘      └────────────────────┘
       │                     │                         │
       │                     │                         │
       ▼                     ▼                         │
┌─────────────┐      ┌─────────────────┐               │
│             │      │                 │               │
│  会话状态   │◀────▶│  数据存储       │◀──────────────┘
│  (JWT)      │      │  (用户信息)     │
│             │      │                 │
└─────────────┘      └─────────────────┘

数据流转详解

  1. 用户界面 → NextAuth API → OAuth提供商
    • 用户在前端界面点击登录按钮,触发认证流程
    • NextAuth API构建OAuth授权URL并重定向用户
    • 用户被引导至第三方OAuth提供商(Google/GitHub)进行身份验证
  2. OAuth提供商 → NextAuth API → 数据存储
    • 用户在OAuth提供商完成身份验证
    • 提供商重定向回NextAuth回调URL,附带授权码
    • NextAuth API使用授权码换取用户信息
    • 用户信息被保存到数据存储中
  3. NextAuth API → 会话状态
    • 认证成功后,NextAuth创建JWT令牌
    • JWT包含必要的用户信息和认证元数据
    • 令牌加密并存储在HTTP-only cookie中
  4. 会话状态 用户界面
    • 前端通过 useSession钩子或 getSession函数获取会话状态
    • 会话状态包含用户身份和权限信息
    • 用户界面根据会话状态调整显示内容(如显示用户资料)
  5. 会话状态 数据存储
    • JWT令牌中包含用户标识符(如UUID)
    • 可通过标识符从数据存储中获取完整用户信息
    • 会话更新时可能涉及数据存储的变更(如更新登录时间)

登录流程(前端到后端)

1. 前端登录入口

文件: app/auth/signin/page.tsx

登录页面是服务端组件,显示登录按钮并检查用户是否已登录:

tsx 复制代码
export default async function SignInPage({
  searchParams,
}: {
  searchParams: { callbackUrl: string | undefined };
}) {
  const session = await auth();
  if (session) {
    return redirect(searchParams.callbackUrl || "/");
  }
  
  // 登录页面UI
}

此组件对应数据流转图中的用户界面(客户端),负责展示登录界面并检查会话状态。

2. 客户端登录按钮

文件: app/auth/signin/client.tsx

客户端组件,处理登录按钮点击事件:

tsx 复制代码
export function SignInButton({ 
  provider, 
  callbackUrl,
  children
}: { 
  provider: string; 
  callbackUrl?: string;
  children: React.ReactNode;
}) {
  return (
    <button
      onClick={() => signIn(provider, { callbackUrl: callbackUrl || '/' })}
      className="..."
    >
      {provider === 'google' && <FaGoogle className="..." />}
      {provider === 'github' && <FaGithub className="..." />}
      <span>{children}</span>
    </button>
  );
}

此组件触发用户界面→NextAuth API 的数据流,调用 signIn函数发起认证请求。

3. NextAuth API路由

文件: app/api/auth/[...nextauth]/route.ts

处理NextAuth的API请求:

tsx 复制代码
import NextAuth from "next-auth";
import { authOptions } from "@/auth/config";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

此文件对应数据流转图中的NextAuth API(服务端路由),处理所有认证相关的HTTP请求,包括:

  • 认证请求(重定向到OAuth提供商)
  • 回调处理(接收OAuth提供商的回调)
  • 会话查询(前端获取会话状态)

4. NextAuth配置

文件: auth/config.ts

配置NextAuth认证选项,包括:

  • 认证提供商(Google、GitHub)
  • 登录回调
  • JWT处理
  • Session处理

关键代码:

tsx 复制代码
export const authOptions: NextAuthConfig = {
  providers,
  pages: {
    signIn: "/auth/signin",
  },
  callbacks: {
    async signIn({ user, account, profile }) { ... },
    async redirect({ url, baseUrl }) { ... },
    async session({ session, token }) { ... },
    async jwt({ token, user, account }) { ... },
  },
};

这个配置文件定义了NextAuth API 如何与OAuth提供商数据存储 交互,以及如何处理会话状态

5. NextAuth实例

文件: auth/index.ts

创建NextAuth实例并导出相关函数:

tsx 复制代码
import NextAuth from "next-auth";
import { authOptions } from "./config";

export const { handlers, signIn, signOut, auth } = NextAuth(authOptions);

这些导出的函数促成了数据流转图中的多个流程:

  • signIn:用户界面→NextAuth API
  • auth:NextAuth API→会话状态
  • signOut:用户界面→NextAuth API→会话状态(清除)

6. 用户信息处理

文件: services/user.ts

处理用户信息保存逻辑:

tsx 复制代码
export async function saveUser(user: User) {
  try {
    const existUser = await findUserByEmail(user.email);
    if (!existUser) {
      await insertUser(user);
    } else {
      user.id = existUser.id;
      user.uuid = existUser.uuid;
      user.created_at = existUser.created_at;
    }
    return user;
  } catch (e) {
    throw e;
  }
}

此服务对应数据流转图中的NextAuth API→数据存储流程,负责将OAuth提供商返回的用户信息持久化到数据存储中。

7. 数据存储

文件: models/user.ts

示例项目使用内存数组存储用户信息,实际项目应使用数据库:

tsx 复制代码
// 演示用简化版本,实际项目中应使用数据库
const users: User[] = [];

export async function findUserByEmail(email: string): Promise<User | null> { ... }
export async function findUserByUuid(uuid: string): Promise<User | null> { ... }
export async function insertUser(user: User): Promise<User> { ... }

此模块实现了数据流转图中的**数据存储(用户信息)**组件,提供用户数据的CRUD操作。

登录流程详解

  1. 用户点击登录按钮
    • 前端调用 signIn(provider)函数
    • NextAuth.js将用户重定向到第三方OAuth提供商(Google/GitHub)
    • 数据流:用户界面→NextAuth API→OAuth提供商
  2. 第三方认证
    • 用户在第三方平台完成认证
    • 第三方平台将用户重定向回应用的回调URL
    • 数据流:OAuth提供商→NextAuth API
  3. 处理回调
    • NextAuth.js API接收回调请求
    • 调用 jwt回调处理令牌
    • 保存用户信息到后端存储
    • 数据流:NextAuth API→数据存储NextAuth API→会话状态
  4. 创建会话
    • 调用 session回调构建会话信息
    • 返回包含用户信息的会话对象
    • 数据流:会话状态**↔**用户界面
  5. 完成登录
    • 用户被重定向到指定的回调URL或首页
    • 前端可通过 useSession钩子或 auth()函数访问会话信息
    • 数据流:会话状态**↔用户界面 和**会话状态**↔**数据存储

JWT与会话状态管理

JWT(JSON Web Token)在认证流程中扮演核心角色,对应数据流转图中的**会话状态(JWT)**节点:

  1. JWT创建
    • 用户成功认证后,NextAuth创建包含用户信息的JWT令牌
    • JWT中存储必要的用户信息(如UUID、头像URL等)
    • 数据流:NextAuth API→会话状态
  2. JWT存储
    • JWT令牌加密后存储在HTTP-only cookie中
    • 浏览器每次请求自动发送cookie,实现无状态认证
    • 数据流:会话状态**↔**用户界面
  3. 会话构建
    • 服务端通过 auth()函数解析JWT令牌获取会话信息
    • 客户端通过 useSession()钩子访问会话状态
    • 数据流:会话状态**↔**用户界面
  4. 会话与数据存储交互
    • 会话中的用户标识可用于从数据存储获取完整用户信息
    • 可通过会话中的用户ID更新数据存储中的用户信息
    • 数据流:会话状态**↔**数据存储

类型扩展

NextAuth类型扩展,支持JWT和Session中的自定义字段:

tsx 复制代码
declare module "next-auth" {
  interface JWT {
    user?: {
      uuid?: string;
      nickname?: string;
      avatar_url?: string;
      created_at?: string;
    };
  }

  interface Session {
    user: {
      uuid?: string;
      email?: string | null;
      name?: string | null;
      nickname?: string | null;
      avatar_url?: string | null;
      created_at?: string | null;
    }
  }
}

用户类型定义,对应数据存储中的用户数据结构:

tsx 复制代码
export interface User {
  id?: number;
  uuid?: string;
  email: string;
  created_at?: string;
  nickname: string;
  avatar_url: string;
  signin_type?: string;
  signin_ip?: string;
  signin_provider?: string;
  signin_openid?: string;
}

安全考虑

  1. 环境变量:OAuth客户端ID和密钥存储在环境变量中
  2. 重定向检查:验证重定向URL的合法性
  3. JWT令牌:使用JWT保存会话状态,加密存储防篡改
  4. 无密码存储:使用OAuth方式不需要存储用户密码
  5. HTTP-only Cookie:JWT存储在HTTP-only cookie中,防止JavaScript访问
  6. CSRF保护:NextAuth内置CSRF令牌验证机制

数据流转优化建议

  1. 数据库集成
    • 将内存存储替换为持久化数据库(MongoDB、PostgreSQL等)
    • 优化数据存储与NextAuth API的交互性能
  2. 令牌刷新机制
    • 实现OAuth访问令牌自动刷新功能
    • 延长用户会话有效期,减少重复登录
  3. 缓存层引入
    • 在数据存储与会话状态之间添加缓存层(如Redis)
    • 减轻数据库负担,提高频繁会话查询性能
  4. 前端状态管理
    • 优化前端会话状态管理,减少不必要的API调用
    • 实现优雅的会话过期处理和自动重新认证

参考

相关推荐
写写闲篇儿5 小时前
微软面试之白板做题
面试·职场和发展
程序员爱钓鱼5 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
PineappleCoder5 小时前
工程化必备!SVG 雪碧图的最佳实践:ID 引用 + 缓存友好,无需手动算坐标
前端·性能优化
JIngJaneIL6 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码6 小时前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web
澄江静如练_6 小时前
列表渲染(v-for)
前端·javascript·vue.js
liang_jy6 小时前
Android LaunchMode
android·面试
JustHappy7 小时前
「chrome extensions🛠️」我写了一个超级简单的浏览器插件Vue开发模板
前端·javascript·github
Loo国昌7 小时前
Vue 3 前端工程化:架构、核心原理与生产实践
前端·vue.js·架构