Skip to content

这是什么项目 ​

这是一个从零理解 OAuth 2.0 / OpenID Connect 登录流程的学习项目:一个自己的授权服务器 (OpenIddict + ASP.NET Core Identity)+ 一套 React 写的登录 / 同意 / 登出页面 + 一个演示用的 SPA 客户端, 然后把整条登录链路逐步拆开讲。

三个进程,三个角色 ​

角色目录地址干什么
OP(授权服务器 + 资源服务器)backend/Yagang.Identity/Yagang.Identity.Apihttps://localhost:7016认证用户、弹同意页、签发 code/token、校验 access token 并提供 /weatherforecast
SPA 客户端(要登录的那个应用)demos/yagang-client-demohttp://localhost:5173用 oidc-client-ts 发起授权码 + PKCE 流程,拿 token 调 API
React 登录前端frontend/yagang-identity-app构建产物同源托管在 OP 的 wwwroot(页面 URL:/Account/Login、/Account/Consent、/Account/Logout、/Account/ExternalLoginConfirm)登录页 / 同意页 / 登出确认页 / 外部账号确认页

为什么登录页要"同源托管"

登录页如果和 OP 不同源(比如独立部署在另一个端口/域名),就要处理 CORS、SameSite cookie、第三方 cookie 被浏览器拦截等一堆问题。 把构建产物输出到 OP 的 wwwroot 后,登录页与 OP 同源: 表单 POST 直接带 cookie、fetch('/Account/ExternalProviders') 这类同源 XHR 也不需要任何 CORS 配置。

一次登录会发生什么(极简版) ​

1. 客户端 重定向 → GET /connect/authorize?client_id=client-demo&...&code_challenge=...
2. OP 未认证 → 302 /Account/Login?ReturnUrl=<上面那个地址>
3. 用户在 React 登录页输入 alice / P@ssw0rd-001! → POST /Account/Login → OP 种 Identity cookie
4. 浏览器回到 /connect/authorize(这次已认证)
5. OP 发现「该用户 + 该客户端 + 这些 scope」还没有永久授权 → 302 /Account/Consent?...
6. 用户点「同意」→ POST /connect/authorize(submit.Accept) → OP 写永久授权 → 302 回客户端 redirect_uri?code=...
7. 客户端用 code + PKCE verifier → POST /connect/token → 拿到 access_token / id_token(含 role=admin)
8. 客户端带 access_token 调 GET /weatherforecast → 200
9. 登出:GET /connect/logout → React 登出确认页 → POST /connect/logout → 清 cookie → 302 回客户端

每一段都在流程总览里展开成一页。

学习路径 ​

  1. 项目架构:先搞清楚"哪一层负责什么",以及 cookie / code / access token 三层认证怎么区分。
  2. 本地跑起来:把 OP + 客户端跑起来,边读边点。
  3. 流程总览:完整时序 + 每步的请求/响应形态。
  4. ①→⑥ 各阶段:逐段看代码与判定条件。
  5. 代码走读:回到 Program.cs、四个控制器、React 四页,看实现细节。
  6. 表单登录与 loading(业界做法):为什么大厂登录页都是真 <form>,以及用 form 怎么做出"转圈"效果。
  7. 坑速查:真实踩过的坑,含报错信息与根因。

关键结论先放这里 ​

三句话记住这个项目

  1. 服务端 = OpenIddict 授权服务器 + ASP.NET Core Identity 会话:协议侧由 AuthorizationController 承担,外部提供方回调在 AuthenticationController,React 页面需要的东西(登录提交、防伪 token、外部登录入口与落地)全部集中在 Controllers/AccountController.cs。
  2. 登录 = Identity 应用 cookie(SignInManager 种的 .AspNetCore.Identity.Application,默认名),授权 = OpenIddict 签发的 code / token,两者是独立的两个层次。
  3. 同意状态存在 oi_ath 表(永久授权)。示例库每次启动会清空 OpenIddict 四张表 → 重启后第一次授权一定会再弹同意页。

OpenIddict + ASP.NET Core Identity 授权服务器,React 登录页,逐步拆解登录链路