OAuth2 入门
1. 一句话简介
OAuth2 是一个开放授权(Authorization)协议,核心解决的问题是:让第三方应用在不接触用户明文密码的前提下,获得访问用户受保护资源的受限授权。它把「认证(你是谁)」和「授权(你能访问什么)」分离,通过授权服务器(Authorization Server)、资源服务器(Resource Server)、客户端(Client)三方协作完成令牌的分发、验证与资源访问控制。
本 demo 模块(demo-oauth)用 Spring Security OAuth2 落地了完整的授权体系:oauth-authorization-server 负责用 JWT(私钥签名、非对称加密)签发 Access Token,oauth-resource-server 用公钥验签,通过 @EnableAuthorizationServer / @EnableResourceServer 两个注解配合 AuthorizationServerConfigurerAdapter / ResourceServerConfigurerAdapter 实现,并演示了授权码(Authorizaton Code)与密码(Password)两种授权模式,用户与客户端信息均从 MySQL 数据库加载。
2. 什么时候使用
✅ 适用场景
- 第三方应用授权:需要让不信任的第三方 App 访问用户的资源(如用微信/QQ 登录某网站、授权某 App 读取你的相册),且不能把密码交给第三方,OAuth2 的授权码模式是最佳方案。
- 多系统统一认证与授权:在微服务或前后端分离架构中,把认证和授权职责集中到授权服务器统一签发 Token,各资源服务器只负责验签鉴权,避免每服务各自实现登录逻辑(本 demo 即演示了授权服务器与资源服务器分离部署)。
- 无状态鉴权、服务间调用:使用 JWT 作为令牌时,资源服务器只需持有公钥即可本地验签,无需回授权服务器查库,适合水平扩展、跨服务传递身份信息(本 demo 的
OauthResourceTokenConfig即用公钥实现本地验签)。 - 细粒度权限控制:基于角色(
hasRole('ADMIN'))与 Scope(#oauth2.hasScope('READ'))双重维度控制资源访问,实现「能看数据 vs 能改数据」的精细授权(见本 demo 的TestController)。 - 令牌生命周期管理:需要令牌可过期(
access_token_validity_seconds)、可刷新(Refresh Token)、可注销(自定义退出处理)的完整生命周期管控。
❌ 不适用 / 需谨慎
- 自身封闭系统的简单登录:仅同一系统内部、无第三方接入需求时,用 OAuth2 反而带来更多复杂性,Session + CSRF 的经典认证足以胜任,属于过度设计。
- 对实现与配置熟悉度要求高:OAuth2(尤其授权服务器那一侧)配置复杂度高,权限模型、授权模式、Token 存储选择不当容易埋下安全漏洞,学习与调试成本不低,需充分理解协议后再引入。
- 默认 Token 存储有局限:本 demo 的
Oauth2AuthorizationTokenConfig使用InMemoryTokenStore,服务重启后令牌即失效、无法水平扩展;生产环境若用非 JWT 形式令牌必须换用 Redis 等共享存储,否则集群下多实例无法互相校验。 - JWT 一旦签发难立即吊销:JWT 无状态意味着令牌在有效期内即使被泄露也无法即时作废(除非维护黑名单),对高安全要求、需要强管控撤销的场景需谨慎设计黑名单或改用短时效。
- 令牌自认证不依赖授权服务器在线:若资源服务器本地用公钥验签,授权服务器短暂宕机不会立刻影响已签发令牌的校验,但新的令牌获取会失败,需权衡容错与一致性。
3. 常见业务场景
第三方登录(账号打通):网站允许用户用微信、GitHub、Google 等第三方账号登录。用户点击登录后跳转到第三方授权服务器,第三方确认授权后回调返回授权码,网站用授权码换取令牌并访问用户基本信息。这正是 OAuth2 授权码模式,用户密码始终留在第三方,网站无法获取明文,安全可控。
微服务网关统一鉴权:在一个微服务体系中,独立的授权服务器集中负责登录与签名 Token(本 demo 的 :8080),各个微服务作为资源服务器(本 demo 的 :8081)只持公钥验签。新增服务无需重写认证代码,只需配置公钥与资源 ID 即可接入受保护 API,实现了认证能力复用与横切统一。
基于角色与 Scope 的 API 授权:一个客户端申请了 READ,WRITE 两个 Scope,不同用户又拥有不同角色。资源服务器的 TestController 通过 @PreAuthorize("hasRole('ADMIN')") 限制角色、@PreAuthorize("#oauth2.hasScope('READ')") 限制操作范围,实现「谁(角色)能做什么(Scope)」的双重开关,适合权限分层、读写分离的资源系统。
多客户端分别授权:同一用户授权给多个应用,各自持有不同的 client_id / client_secret 与配置的回调地址、授权类型、Token 时效(见数据库 sys_client_details 表)。OAuth2 允许每个客户端独立管理授权,也支持 autoApproveScopes 自动授权避免重复弹窗确认,同时 updateClientSecret 可对单个客户端单独重置密钥,适合面向多应用开放平台的场景。
令牌生命周期与刷新:Access Token 时效较短(本 demo 默认 6000 秒),过期后客户端可用 refresh_token_validity_seconds 对应的 Refresh Token 静默续期,避免频繁重新登录。配合自定义授权确认页(authorization.html)、登录页与退出处理,形成从登录、授权、获牌、访问到登出的完整闭环,适合需要持续在线的移动端 / 长连接应用。
4. 同类技术对比
| 维度 | Spring Security OAuth2(本 demo) | Spring Security + JWT 自研认证 | Spring Authorization Server | 第三方平台 OAuth/OpenID(如微信、GitHub) |
|---|---|---|---|---|
| 定位 | 协议级授权框架,可自建授权/资源服务器 | 应用内认证方案,非协议标准 | Spring 官方新一代授权服务器 | 托管云端认证/授权服务 |
| 授权能力 | 支持授权码/密码/客户端/刷新等完整授权模式与多种 TokenStore | 通常只做登录态签发,无标准授权流程 | 遵循 OAuth2.1/OpenID Connect 标准 | 提供标准授权,由平台托管 |
| 实现复杂度 | 较高(需理解端点/服务/Token 配置) | 低至中(自己维护令牌校验) | 中高 | 最低(对接即用) |
| 无状态/JWT 支撑 | 支持(JWT 非对称公私钥) | 支持(自签自验) | 支持(JWT/JWS 默认) | 支持(由平台签发) |
| 扩展/定制性 | 强(可换 TokenStore、自定义页面、接入 DB) | 强(完全自研) | 强(官方演进) | 弱(受平台能力与限制约束) |
| 运维/依赖 | 需自建并维护授权+资源服务 | 需自行保证鉴权安全性 | 需自建 | 零运维,受平台 SLA 约束 |
| 演进趋势 | Spring Security OAuth2 旧版已进入维护期 | — | 官方主推、替代旧 OAuth2 组件 | 需要外部账号体系时首选 |
| 适用规模 | 中大型自建统一授权体系 | 中小型单体/内部系统 | 新项目自建授权服务器 | 纳入外部生态 / 快速上线 |
选型建议
- 自建、需要完整协议级授权与高可控性:若需要同时掌控授权服务器与资源服务器、定制令牌存储与页面,且愿意承担配置复杂度,选 Spring Security OAuth2(本 demo 的模式);但涉及新项目应优先用官方新一代 Spring Authorization Server,因为旧版
spring-security-oauth2-autoconfigure已进入维护期。 - 内部系统、只需要简单登录态:若仅同一个封闭系统内做认证鉴权、无第三方与多客户端授权需求,用 Spring Security + Session 或自己签发 JWT 即可,无需引入完整 OAuth2,更轻量、更易维护。
- 需接入外部账号生态 / 快速上线:要支持微信、GitHub 等平台账号登录或借助成熟云认证时,直接对接第三方 OAuth2 / OpenID Connect 最省成本,遵循 T-领域标准且零运维。
- 统一的实践组合:许多生产系统采用「第三方 OAuth2 做外部打点 + 自建授权服务器(新一代 Spring Authorization Server)/ JWT 做内部服务间鉴权」,各取所长,兼顾安全与落地成本。