当前位置: 首页 > news >正文

WHAT - NextAuth 登录流程架构

文章目录

  • 1. Provider:认证入口
  • 2. authorize()
  • 3. jwt() Callback
    • 常见写法
  • 4. session() Callback
    • 为什么要两层?
  • 5. auth()
  • 6. Middleware
    • RBAC 权限控制
  • 7. 登录全过程源码视角
    • Step1
    • Step2
    • Step3
    • Step4
    • Step5
    • Step6
    • Step7
    • Step8
    • Step9
  • 8. 真正推荐的企业级写法
  • 9. 具体例子
    • 场景
    • 第一步:authorize()
    • 第二步:jwt()
    • 第三步:session()
    • 为什么还要 Session?
    • 一个真实企业场景
    • 生命周期
    • 最终可以这样理解

在 WHAT - NextAuth 权限认证机制 中我们介绍了 NextAuth 是什么以及优缺点。下一步,最重要的是理解它内部的数据流。

可以把 NextAuth 看成一个认证状态机:

┌─────────────┐ │ Provider │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ authorize │ └──────┬──────┘ │ User ▼ ┌─────────────┐ │ callbacks │ │ jwt() │ └──────┬──────┘ │ Token ▼ ┌─────────────┐ │ callbacks │ │ session() │ └──────┬──────┘ │ Session ▼ ┌─────────────┐ │ auth() │ │ useSession()│ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Middleware │ └─────────────┘

1. Provider:认证入口

Provider 决定用户如何登录。例如 GitHub:

providers:[GitHub({clientId,clientSecret})]

Credentials:

providers:[Credentials({asyncauthorize(credentials){...}})]

本质上:

Provider = 获取用户身份信息

不同 Provider 获取方式不同:

Provider获取用户方式
GitHubOAuth
GoogleOAuth
Credentials用户名密码
EmailMagic Link

2. authorize()

只有 Credentials Provider 有这个步骤。例如:

Credentials({asyncauthorize(credentials){constuser=awaitdb.user.findUnique({email:credentials.email})if(!user){returnnull}returnuser}})

这里返回的 user 非常重要。

return{id:"1",name:"Tom",role:"admin"}

这个对象会流入后续:

authorize() ↓ jwt() ↓ session()

3. jwt() Callback

最核心的 Callback。

callbacks:{asyncjwt({token,user}){returntoken}}

第一次登录:

{token:{},user:{id:"1",role:"admin"}}

后续请求:

{token:{id:"1",role:"admin"},user:undefined}

因为用户信息已经存进 JWT。


常见写法

把数据库字段塞进 JWT:

callbacks:{asyncjwt({token,user}){if(user){token.id=user.id token.role=user.role}returntoken}}

结果:

{"sub":"1","id":"1","role":"admin"}

JWT 会被加密后放到 Cookie。

Cookie ↓ JWT ↓ role=admin

4. session() Callback

客户端拿不到 JWT。客户端拿的是 Session。

callbacks:{asyncsession({session,token}){session.user.id=token.id session.user.role=token.rolereturnsession}}

流程:

JWT ↓ session() ↓ Session

例如:

JWT:

{"id":"1","role":"admin"}

Session:

{"user":{"id":"1","role":"admin"}}

为什么要两层?

因为:

JWT = 服务端真实身份 Session = 暴露给前端的数据

你可以过滤敏感信息:

session.user.role=token.role// 不暴露token.accessToken token.refreshToken

5. auth()

App Router 新版本最重要的 API。

constsession=awaitauth()

内部其实在做:

Cookie ↓ 读取 JWT ↓ 解密 JWT ↓ 执行 session() ↓ 返回 Session

所以:

constsession=awaitauth()

相当于:

constuser=getCurrentUser()

6. Middleware

用于页面保护。

export{authasmiddleware}from"@/auth"

或者:

exportdefaultauth((req)=>{if(!req.auth){returnResponse.redirect("/login")}})

执行流程:

请求页面 ↓ Middleware ↓ 读取 Cookie ↓ 解析 JWT ↓ req.auth ↓ 允许/拒绝

RBAC 权限控制

例如:

exportdefaultauth((req)=>{if(req.auth?.user.role!=="admin"){returnResponse.redirect("/")}})

这样:

admin ✓ user ✗

7. 登录全过程源码视角

以 GitHub 登录为例:


Step1

点击登录

signIn("github")

浏览器跳转:

/auth/signin/github

Step2

重定向 GitHub

GitHub OAuth

用户授权:

Allow

Step3

GitHub 返回 code

callback?code=xxx

Step4

NextAuth 交换 Token

code ↓ access_token

调用 GitHub API:

GET /user

获得:

{"id":123,"login":"tom"}

Step5

生成 User

内部统一转换:

user={id:"123",name:"Tom"}

Step6

执行 jwt()

jwt({token,user})

得到:

{"id":"123","role":"admin"}

Step7

生成 Session

session({session,token})

得到:

{"user":{"id":"123","role":"admin"}}

Step8

写 Cookie

Set-Cookie: next-auth.session-token=...

Step9

后续请求

Cookie ↓ JWT ↓ Session ↓ auth()

完成身份恢复。


8. 真正推荐的企业级写法

很多人会这样:

session.user=user

把整个数据库用户对象塞进去。

这是不推荐的。更推荐:

jwt(){token.id=user.id token.role=user.role}session(){session.user.id=token.id session.user.role=token.role}

JWT 中只保留:

{id,role,tenantId}

即:

Identity Claims

而不是:

完整 User Profile

这样:

  • Cookie 更小
  • Session 更快
  • 权限控制更清晰
  • 用户资料更新不需要重新登录

从架构角度看,NextAuth 最核心的一句话是:

Provider ↓ User ↓ jwt() ↓ JWT(Token) ↓ session() ↓ Session ↓ auth() ↓ Middleware / Server Component / Client Component

理解这条链路后,基本就能看懂 NextAuth 80% 的源码和大部分企业项目中的认证实现。

9. 具体例子

我们用一个完整例子来理解。

场景

数据库里有一个用户:

{"id":"1001","name":"Tom","email":"tom@gmail.com","role":"admin","department":"研发部","salary":50000}

用户通过 Credentials 登录。


第一步:authorize()

asyncauthorize(credentials){constuser=awaitdb.user.findUnique(...)returnuser}

此时得到:

{"id":"1001","name":"Tom","email":"tom@gmail.com","role":"admin","department":"研发部","salary":50000}

这个对象叫:

User

第二步:jwt()

NextAuth 调用:

callbacks:{asyncjwt({token,user}){if(user){token.id=user.id token.role=user.role}returntoken}}

此时生成:

{"id":"1001","role":"admin"}

这就是:

JWT Payload

JWT 被存入 Cookie:

Cookie next-auth.session-token ↓ { "id": "1001", "role": "admin" }

为什么只存这两个字段?

因为 JWT 每次请求都要带:

浏览器 ↓ Cookie ↓ 服务器

如果把全部用户信息塞进去:

{"id":"1001","name":"Tom","email":"...","department":"...","salary":50000,...}

Cookie 会越来越大。所以 JWT 通常只存:

身份标识 + 权限信息

例如:

{"id":"1001","role":"admin"}

第三步:session()

之后用户访问页面:

constsession=awaitauth()

NextAuth 会:

Cookie ↓ 解析 JWT ↓ 执行 session()

此时:

session({session,token})

参数:

token={"id":"1001","role":"admin"}

你决定给前端暴露什么:

asyncsession({session,token}){session.user.id=token.id session.user.role=token.rolereturnsession}

得到:

{"user":{"id":"1001","role":"admin"}}

这就是:

Session

前端能拿到的对象。


为什么还要 Session?

因为:

JWT = 服务端身份凭证 Session = 前端用户信息

JWT 是内部使用的。Session 是公开给 React 页面使用的。


一个真实企业场景

假设 JWT:

{"id":"1001","role":"admin","tenantId":"company-a","accessToken":"xxxxx"}

这里:

accessToken

可能是调用第三方系统的凭证。不能暴露给浏览器。


因此:

asyncsession({session,token}){session.user.id=token.id session.user.role=token.rolereturnsession}

前端拿到:

{"user":{"id":"1001","role":"admin"}}

而:

{"accessToken":"xxxxx"}

永远不会暴露。


生命周期

登录第一次:

authorize() ↓ user ↓ jwt() ↓ JWT生成

以后每次请求:

Cookie ↓ JWT ↓ jwt() ← 再次执行 ↓ session() ↓ Session

注意:

第一次登录:

jwt({token,user})

user有值:

{"id":"1001","role":"admin"}

第二次刷新页面:

jwt({token,user})

此时:

user===undefined

因为用户已经登录了。NextAuth 直接从 Cookie 中恢复 JWT。

所以常见写法:

asyncjwt({token,user}){if(user){token.id=user.id token.role=user.role}returntoken}

就是:

第一次登录 ↓ 把 User 写进 JWT 后续请求 ↓ 直接复用 JWT

最终可以这样理解

数据库用户(User) { id:1001, role:'admin', salary:50000 } ↓ jwt() ↓ JWT(Token) { id:1001, role:'admin' } ↓ session() ↓ Session { user:{ id:1001, role:'admin' } }

三者职责:

对象作用谁能访问
User数据库完整用户服务端
JWT(Token)身份凭证、权限声明服务端(存于 Cookie)
Session给前端展示的用户信息前端 + 服务端

可以记一句口诀:

User 是原始数据 JWT 是身份证 Session 是展示给前端看的名片

这也是为什么 NextAuth 的核心 Callback 永远是:

user ↓jwt()↓ token ↓session()↓ session

本质上是在做一次用户对象 → 身份声明(Claims) → 前端可见数据(View Model)的转换。

http://www.rkmt.cn/news/1475322.html

相关文章:

  • 别再瞎点Debug了!ZYNQ SDK与PL联合调试的保姆级流程(含ILA触发条件详解)
  • 夸克网盘批量管理终极指南:3分钟掌握高效文件处理技巧
  • 2026实测南京黄金回收市场,禹竞深耕本地多年,口碑和实力双在线 - 奢侈品交易观察员
  • QT自定义控件之热换站远程监控系统
  • 沈阳购宠全攻略|东北严寒大风气候避坑指南 + 伴西西浑南、沈河双直营店精选 5 家正规门店 - 资讯速览
  • 全国染料厂主要分布在哪些地区?产区分布与产能观察
  • 大模型“睡眠”机制:提升推理能力,训练成本却线性增长?
  • 手把手教你用ESP8266+Arduino+PubSubClient库,5分钟搞定OneNet旧版MQTT接入(附完整代码)
  • 企业法务部搭建诉讼管理看板的完整指南:从数据收集到可视化监控
  • AT91SAM9260 Nor Flash Bootstrap移植实战:从零适配启动引导程序
  • MCprep终极指南:让Minecraft动画制作变得简单快速
  • 2026济南黄金回收行业领军巨头!合扬稳居行业标杆领跑全城回收市场 - 开心测评
  • 从电热水壶维修看电子产品可靠性设计与可维护性
  • 手把手教你用STM32F103和LM358搭建PT100测温电路(附完整代码与调试心得)
  • 2025-2026年全球岗位外包公司推荐:五大口碑产品评测核心能力选择指南价格
  • 如何在Mac上零成本实现专业医学影像分析?Horos免费开源工具终极指南
  • Simple Live:跨平台直播聚合应用终极指南,告别频繁切换的烦恼
  • Windows右键菜单终极管理指南:如何快速掌握ContextMenuManager
  • MATLAB内点法无功优化代码包:含IEEE14节点完整算例与逐行中文注释
  • GNOME扩展管理器终极指南:一站式安装、管理与升级
  • 体育场馆预约系统小程序/网站开发方案|功能详解+个人开发报价+合作全流程
  • 【C语言】实现简单动态数组(线程安全)
  • 探索oled高级显示:借助快马ai模型生成动画与特效代码
  • 嘴炮Hermes:我干完了!实际啥也没做,咋整?
  • 当Git操作失误时,如何优雅地按下“撤销“键?
  • 2026 成都黄金回收商户实力测评,收的顶全国连锁高价夺冠稳居同城榜首 - 奢侈品回收评测
  • 上班族 AI 学习方案 第九周Agent 智能体原理 + 实操LangChain
  • deepseek 适配了 华为升腾 是不是 用了类似Megatron-LM deepSpeed框架的??
  • 智能进化算法:借助快马平台AI模型优化杜鹃算法的莱维飞行与参数策略
  • 工程师思维:冗余|冗余越多,容错能力越强