CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
OAuth 2.0 与 OpenID Connect/课时/综合练习
RawBlame
课时目录12 节

OAuth 2.0 与 OpenID Connect

  1. 01OAuth 是什么
  2. 02授权码流
  3. 03PKCE 与 SPA
  4. 04OpenID Connect
  5. 05Scope 与权限
  6. 06Refresh Token
  7. 07Spring OAuth2 Client
  8. 08Resource Server
  9. 09第三方登录
  10. 10OAuth vs 本站 JWT 登录
  11. 11常见安全问题
  12. 12综合练习

综合练习

授权码 + JWT Resource Server 实验。

40 分钟

综合练习

这一课你将能做到

  1. 独立跑通最小闭环:GitHub OAuth App → Spring OAuth2 Client → 映射本地用户 → 签发本站 JWT → 前端 callback 落 cookie
  2. 可选分支:为 /api/v1/** 挂上 Resource Server,用带 tracks:read 的 access token 读接口,验证 401/403 行为
  3. 交出验收清单:密码轨与 OAuth 轨互不破坏、安全项全勾、README 写清「何时用哪种 token」

为什么要学

前 11 课拆零件,这一课装整机。判断你是否真学会的标准,不是能背多少术语,而是:

  • 点一下按钮,能从 platform 跳到 GitHub 再带着本站会话回来;
  • Network 里每一条请求的「角色」你都说得清:这是 authorize、这是 callback、这是换票、这是 RS 验签;
  • 在仓库里搜 SECRET 搜不到真实值;
  • 无 token 401、缺 scope 403、关掉 OAuth 密码登录照常工作。

做完这个练习,backend 的 OAuth 实验节点就可以打勾,并留下一段「何时用哪种 token」的团队文档——这是从「学过」到「用过」的分界线。

先建立图像

[Next platform]「GitHub 登录」按钮
        │ window.location → backend
        ▼
[Spring] /oauth2/authorization/github        ← Client 链:生成 state、302
        ▼
[GitHub IdP] 登录 + 同意
        ▼
[Spring] /login/oauth2/code/github           ← 框架验 state、换 token、拉用户
        │ OAuth2LoginSuccessHandler
        │   findOrCreate(provider, externalId)
        │   jwtUtil.sign(uid, role)          ← 本站自研 JWT
        ▼
[Next] /login/callback?token=...             ← AuthProvider 写 HttpOnly cookie
        ▼
后续所有请求 = 与密码登录完全相同的站内凭证轨道

可选分支:
[外部 Client] Bearer(IdP 签发, 含 tracks:read) → /api/v1/tracks(Resource Server 链)

跟着做

步骤 1:IdP 注册

GitHub → Developer settings → OAuth Apps → New
Homepage:      http://localhost:3000
Callback URL:  http://localhost:8080/login/oauth2/code/github
Client ID / Secret → 写入环境变量 GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET

自检点:Callback URL 与 Spring 默认路径逐字一致;secret 未出现在任何将被提交的文件里。

步骤 2:Client 配置与成功处理器

spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: ${GITHUB_CLIENT_ID}
            client-secret: ${GITHUB_CLIENT_SECRET}
            scope: read:user,user:email

实现 OAuth2LoginSuccessHandler(完整代码见第 7 课):findOrCreateFromOAuth("github", externalId, attrs) → jwtUtil.sign(...) → 302 回 frontendUrl + "/login/callback?token=" + URLEncoder.encode(token, UTF_8)。

启动 backend,浏览器访问 http://localhost:8080/oauth2/authorization/github,应能走完授权并回到前端。

步骤 3:前端闭环

登录页加「GitHub 登录」按钮 → 跳 backend 授权入口
/login/callback 页:读取 token → AuthProvider 写入与密码登录相同的 cookie → 跳首页
验收:刷新后仍显示已登录;带 cookie 调学员/管理接口返回 200

步骤 4:安全门禁自检(逐项打勾)

[ ] state 由框架/实现校验,一次性作废
[ ] redirect 无通配,IdP 后台只有精确 URI
[ ] 仓库搜 SECRET 无真实值,secret 走环境变量
[ ] 前端无 NEXT_PUBLIC_ 类机密
[ ] 日志无完整 token(已打码)
[ ] 联邦用户默认最低权限 USER

步骤 5:(可选)挂上 Resource Server

# 中文注释:开放 API 前缀独立成链(完整配置见第 8 课)
spring.security.oauth2.resourceserver.jwt.issuer-uri: https://你的AS
# 中文注释:用 IdP 发出的、含 tracks:read 的 access 调开放接口
curl -i http://localhost:8080/api/v1/tracks \
  -H "Authorization: Bearer OAUTH_ACCESS_JWT"

# 缺 scope → 403;无 token → 401
curl -i -X POST http://localhost:8080/api/v1/tracks \
  -H "Authorization: Bearer READ_ONLY_TOKEN" \
  -H "Content-Type: application/json" -d '{"slug":"demo"}'

步骤 6:回归密码轨(必做)

# 中文注释:OAuth 配好后,密码登录必须仍然可用——双轨并存的生命线
curl -i -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"你的密码"}'

curl -i http://localhost:8080/admin/tracks \
  -H "Authorization: Bearer <返回的本站JWT>"

再验证一次「关掉 OAuth」(注释掉 registration 或用配置开关禁用)后密码轨不受影响。

步骤 7:写一段团队 README

何时用哪种 token:
  站内会话(platform/admin)→ 本站自研 JWT,/auth/login 或联邦登录后签发
  调 GitHub/Google API      → IdP 的 access_token,仅后端持有
  第三方调我们的开放 API     → IdP 签发的 OAuth access(scope 限定),走 RS 链
  三者互不通用;排错先看请求路径确定凭证种类

讲清楚要点

  1. 闭环跨三个系统(前端、后端、IdP),排错按箭头逐步看 Network,不要只盯后端日志——callback 没到、state 失效、换票 500、前端丢参,症状全不同。
  2. 本站业务授权在自家体系里:联邦成功必须映射用户并签发本站 JWT,而不是让前端拿着 GitHub 的 token 调 admin。
  3. Resource Server 是另一个角色:可选、独立成链、路径分区;别和 oauth2Login 拧成一个「万能开关」却说不清哪条链管哪些路径。
  4. 安全清单没勾完等于练习没完成:能演示只是及格线,secret/redirect/日志项是上线门禁。
  5. 双轨并存的关键是回归:每次改 Security 配置都重跑一遍密码登录 + 受保护接口,防「新轨上线、旧轨阵亡」。
  6. 文档留给未来的自己:三种 token 的适用路径写清楚,三个月后接手的人(或你自己)会感谢现在的你。

你可能会懵的地方

  • callback 到了 Spring 却 500? 顺序排查:secret 环境变量注入了吗;服务器时间准吗(state/码过期误判);scope 对吗;SuccessHandler 里有没有空指针(GitHub 邮件可能为 null)。建议先摘掉自定义 handler 跑通框架默认流程,再挂回钩子。

  • 前端 callback 页拿不到 token? 三类原因:redirect 时没做 URL 编码(token 含 +/= 被截断);localhost 与 127.0.0.1 混用导致 cookie 域不一致;callback 页与 API 跨域时没带 credentials。逐项对齐域名与编码。

  • GitHub 第二次登录不弹同意页? 正常——已授权应用直接静默发 code,体验更好。想重新看到同意页:GitHub Settings → Applications 撤销授权,再走一遍。

  • 加了 Resource Server 后 /admin 全 401? RS 链的 securityMatcher 误伤(写成了 /**),或链的 @Order 排错把 /admin/** 抢先匹配。改回 /api/v1/** 并核对链顺序(第 8 课卡点急救同款)。

  • 练习做到一半想直接上线? 先补三件事:生产 HTTPS 域名与生产专用 OAuth App、日志脱敏核查、密码轨回归。三项没做之前,只在本地演示,不部署。

错法对照

错法问题正解
只做后端跳转,不接 platform 会话联邦登录没有落地成可用状态完成「映射用户 → 发 JWT → 前端 cookie」闭环
验收只看「能打开 GitHub 页」只完成了流程前半段以「本地已登录态 + 受保护接口 200」为准
secret 写进仓库「作业方便」机密泄露,练习反成反面教材环境变量 + .env.example + gitignore
RS 与 admin 链混在一个过滤器里两套凭证互相干扰,401 成谜securityMatcher 分区 + 注明链序
不做密码轨回归新轨上线踩死旧轨每次改 Security 都跑 admin 登录用例

卡点急救

  1. 现象:完整走完 GitHub 同意后,后端日志报 invalid_grant 或 code 相关错误。最常见是 code 被用了两次:前端刷新了 callback 页、或同时开了两个标签页共享 session。重新发起登录即可;开发期避免刷新 callback URL 本身。
  2. 现象:联邦登录成功,前端 URL 里能看到 token,但 cookie 没写上。检查 callback 页写 cookie 的 domain/path 与后续请求是否一致;确认用 HttpOnly cookie 的方案时写入动作发生在同源请求里(BFF/服务端写),而不是前端 document.cookie。
  3. 现象:想在演示里展示「缺 scope 的 403」,但总是 401。你拿的可能是自研 JWT 在打 RS 链,或 IdP 签的 token 根本没带 tracks:read。先确认凭证种类(issuer 是 IdP),再检查 IdP 侧 scope 配置是否真的发放了该 scope。

面试常问

  • 描述你做过的 OAuth 登录闭环(面试复述模板)? 按四角色讲:前端按钮跳 /oauth2/authorization/github(框架生成 state 并 302);用户在 IdP 同意后回 callback;框架校验 state、用 code+secret 换 token、拉用户信息;我的 SuccessHandler 按 provider+externalId findOrCreate 本地用户,签发站内 JWT 回跳前端写 HttpOnly cookie;后续与密码登录同轨。补一句安全设计:secret 走环境变量、redirect 精确白名单、日志打码。

  • 如果让你给这个系统加开放 API,怎么演进? 加 spring-boot-starter-oauth2-resource-server,/api/v1/** 独立成链,issuer-uri 指向授权服务器;scope 设计(tracks:read/write)+ @PreAuthorize 控制;开放凭证与站内会话严格分区;文档化 scope 契约。体现「角色分明、路径分区、凭证不混用」的架构意识。

  • 上线前你怎么证明 OAuth 部分是安全的? 拿清单说话:state 一次性校验、PKCE(若含公共客户端)、redirect 精确匹配、secret 扫描通过、日志脱敏、refresh 轮换与撤销演练、密码轨回归通过。安全靠可复现的检查动作,不靠口头承诺。

自测三问

  1. 用五步描述你的最小闭环(从按钮点击到本站会话建立)。
  2. 三种 token(本站 JWT、IdP access、refresh)各应用在哪类路径?
  3. 上线前安全清单,你如何证明「做过」而不只是「知道」?

小结一句话

OAuth 实验闭环 = IdP 认人 + Spring Client + 本地用户映射 + 本站 JWT 会话(可选 Resource Server 开放分支);双轨回归 + 安全清单全勾,才算真正学完。

延伸

  • Spring Security:OAuth2 Client
  • oauth.net:OAuth 2.0
  • 下一步方向:把本 track 的能力继续往外推——① 自建授权服务器(Spring Authorization Server,把「发票方」也掌握在自己手里);② 企业 SSO 与 OIDC 多租户(对接公司 IdP);③ 开放平台化(开发者后台、scope 申请审批、文档门户)。建议从①开始:Spring Authorization Server 官方快速上手文档是最好的下一段路。
加载本节练习…
← 上一课常见安全问题