摘要:开放授权(
OAuth):是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限。重点是 OAuth2 的四种角色和客户端的四种授权模式。
目录
[TOC]
OAuth2
开放授权(OAuth):是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限。
- 最终目的是:为第三方应用颁发一个有时效性的令牌 Token,使得第三方应用能够(通过该令牌)获取相关的资源,如该用户在某一网站上存储的私密资源(如照片)。
- OAuth 允许用户提供一个令牌(access token),而不是用户名和密码来访问(存放在特定服务提供者的)数据。
- 而 OAuth 2.0 是对 OAuth 1.0 的完全重新设计,更快、更容易实现,OAuth 1.0 已经被废弃。
OAuth 2.0 常用场景:
- 比较常用的场景就是第三方登录;
- 另外,也常见于支付场景(微信支付、支付宝支付)和开发平台(微信开放平台、阿里开放平台等)。
参考:
阮一峰提供了几篇关于 OAuth2.0 非常不错的文章,推荐胖友去从瞅瞅。
同时,本文也会直接引用它的内容,方便胖友统一理解。
- 《理解 OAuth2.0》
- 《OAuth2.0 的一个简单解释》
- 《OAuth2.0 的四种方式》
- https://mp.weixin.qq.com/s?__biz=MzI1NDY0MTkzNQ==&mid=2247488209&idx=2&sn=19b1e44fbb1f4c1210f0fa92a618d871&scene=21#wechat_redirect,四种认证流程举例
角色
在 OAuth2.0 中,有如下角色:
Resource Owner:资源所有者。最终用户,他有访问资源的账号与密码。- 可以简单理解成人,她在使用 Client 访问资源。
User Agent:用户代理,Client 客户端。它请求资源服务器时,会带上访问令牌,从而成功访问资源。- 可以是浏览器、客户端,也可以是内部服务。第三方登录时的当前 APP。
Authorization server:认证(授权)服务器,用于认证用户。如果客户端认证通过,则发放访问资源服务器的令牌。- 第三方登录时的第三方 服务器。
Resource server:资源服务器,,拥有受保护资源。如果请求包含正确的访问令牌,则可以访问资源。- 提供管理后台、客户端 API 的服务,都可以认为是 Resource Server。
- 与认证服务器,可以是同一台服务器,也可以是不同的。
- 第三方登录时的第三方 服务器。这个时候的资源,主要指的是三方开放平台的用户资料等。
OAuth 2.0 第三方登录,授权码模式的认证流程:

客户端的四种授权模式
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。
OAuth 2.0定义了四种授权方式:
- 密码模式(resource owner password credentials):用户向客户端(原生 APP)提供自己的用户名和密码。客户端使用这些信息,向授权服务器索要授权、申请令牌(token)。
- 授权码模式(authorization code):通过客户端的后台服务器(作为代理客户端),与服务提供商的认证服务器进行互动。功能最完整、流程最严密。如常见的第三方平台登录功能。
- 一般情况下,在有客户端的情况下,我们与第三方平台常常采用这种方式。
- 简化模式(implicit):不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码“这个步骤。
- 所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
- 一般用于网站是纯静态页面。
- 客户端模式(client credentials):指客户端以自己的名义,而不是以用户的名义,向授权服务器进行认证。
- 在这种模式中,用户直接向客户端注册,客户端以自己的名义要求授权服务器提供服务,其实不存在授权问题。
- 如,对接微信公众号时。后端服务器就扮演“客户端”的角色,与微信公众号的后端服务器进行交互。
- refresh_token:?
其中,密码模式和授权码模式比较常用。
如何选择:FROM 《深度剖析 OAuth2 和微服务安全架构》

当然,对于黄框部分,对于笔者还是比较困惑的。笔者认为,第三方的单页应用 SPA ,也是适合采用 Authorization Code Grant 授权模式的。例如,《微信网页授权》 :
具体而言,网页授权流程分为四步:
- 1、引导用户进入授权页面同意授权,获取 code
- 2、通过 code 换取网页授权 access_token(与基础支持中的 access_toke n不同)
- 3、如果需要,开发者可以刷新网页授权 access_token,避免过期
- 4、通过网页授权 access_token 和 openid 获取用户基本信息(支持 UnionID 机制)
所以,艿艿猜测,之所以图中画的是 Implicit Grant 的原因是,受 Google 的 《OAuth 2.0 for Client-side Web Applications》 一文中,推荐使用了 Implicit Grant 。
- 当然,具体使用 Implicit Grant 还是 Authorization Code Grant 授权模式,没有定论。
- 笔者,偏向于使用 Authorization Code Grant,对于第三方客户端的场景。
密码模式
认证流程:
- (A)用户向客户端提供用户名和密码。
- (B)客户端将其发给授权服务器,向后者请求令牌。
- (C)授权服务器确认无误后,向客户端提供访问令牌。

认证及验证:

搭建授权服务器
SecurityConfig
通过 Spring Security 提供认证功能
OAuth2AuthorizationServerConfig
创建 OAuth2AuthorizationServerConfig 配置类,进行授权服务器。
-
在类上添加
@EnableAuthorizationServer注解,声明开启 OAuth 授权服务器的功能。 -
同时,继承 AuthorizationServerConfigurerAdapter 类,进行 OAuth 授权服务器的配置。
其中,/oauth/check_token 端点对应 CheckTokenEndpoint 类,用于校验访问令牌的有效性。
- 在客户端访问资源服务器时,会在请求中带上访问令牌。
- 在资源服务器收到客户端的请求时,会使用请求中的访问令牌,找授权服务器确认该访问令牌的有效性。
1 | |
简单测试
执行 AuthorizationServerApplication 启动授权服务器。下面,使用 Postman 模拟一个 Client。
Basic Auth
① POST 请求 http://localhost:8080/oauth/token 地址,使用密码模式进行授权。


请求说明:
- 通过 Basic Auth 的方式,填写
client-id+client-secret作为用户名与密码,实现 Client 客户端有效性的认证。 - 请求参数
grant_type为"password",表示使用密码模式。 - 请求参数
username和password,表示用户的用户名与密码。
响应说明:
- 响应字段
access_token为访问令牌,后续客户端在访问资源服务器时,通过它作为身份的标识。 - 响应字段
token_type为令牌类型,一般是bearer或是mac类型。 - 响应字段
expires_in为访问令牌的过期时间,单位为秒。 - 响应字段
scope为权限范围。
友情提示:
/oauth/token对应 TokenEndpoint 端点,提供 OAuth2.0 的四种授权模式。感兴趣的胖友,可以后续去撸撸。
check token
② POST 请求 http://localhost:8080/oauth/check_token 地址,校验访问令牌的有效性。如下图所示:


搭建资源服务器
配置文件
创建 application.yml 配置文件,添加 Spring Security OAuth 相关配置。
-
①
security.oauth2.client配置项,OAuth2 Client 配置,对应 OAuth2ClientProperties 类。在这个配置项中,我们添加了客户端的client-id和client-secret。- 为什么要添加这个配置项呢?因为资源服务器会调用授权服务器的
/oauth/check_token接口,而考虑到安全性,配置了该接口需要进过客户端认证。
友情提示:这里艿艿偷懒了,其实单独给资源服务器配置一个 Client 的
client-id和client-secret。我们可以把资源服务器理解成授权服务器的一个特殊的客户端。 - 为什么要添加这个配置项呢?因为资源服务器会调用授权服务器的
-
②
security.oauth2.resource配置项,OAuth2 Resource 配置,对应 ResourceServerProperties 类。这里,通过
token-info-uri配置项,设置使用授权服务器的/oauth/check_token接口,校验访问令牌的有效性。 -
③
security.access-token-uri配置项,是我们自定义的,设置授权服务器的oauth/token接口,获取访问令牌。因为稍后我们将在 LoginController 中,实现一个/login登录接口。
1 | |
OAuth2ResourceServerConfig
创建 OAuth2ResourceServerConfig 类,进行资源服务器。
- ① 在类上添加
@EnableResourceServer注解,声明开启 OAuth 资源服务器的功能。- 同时,继承 ResourceServerConfigurerAdapter 类,进行 OAuth 资源服务器的配置。
- ②
#configure(HttpSecurity http)方法,设置 HTTP 权限。这里,我们设置/login接口无需权限访问,其它接口认证后可访问。- 这样,客户端在访问资源服务器时,其请求中的访问令牌会被资源服务器调用授权服务器的
/oauth/check_token接口,进行校验访问令牌的正确性。
- 这样,客户端在访问资源服务器时,其请求中的访问令牌会被资源服务器调用授权服务器的
1 | |
授权码模式
授权码模式的认证流程:
(A)用户打开客户端以后,客户端跳转到到授权服务器,要求用户给予授权。
(B)用户同意给予客户端授权。(授权服务器将跳转到客户端事先指定的”重定向 URI”(Redirection URI),同时附上一个授权码)
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。(这一步是在客户端的后台的服务器上完成的,对用户不可见)
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
下图是 Slack OAuth 2.0 第三方登录的示意图:

另一种步骤:
- (A)用户访问客户端,后者将前者跳转到到授权服务器。
- (B)用户选择是否给予客户端授权。
- (C)假设用户给予授权,授权服务器将跳转到客户端事先指定的”重定向 URI”(Redirection URI),同时附上一个授权码。
- (D)客户端收到授权码,附上早先的”重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
- (E)认证服务器核对了授权码和重定向 URI,确认无误后,向客户端发送访问令牌。

简化模式
步骤:
- (A)用户访问客户端,后者将前者跳转到到授权服务器。
- (B)用户选择是否给予客户端授权。
- (C)假设用户给予授权,授权服务器将用户导向客户端指定的”重定向URI”,并在 URI 的 Hash 部分包含了访问令牌。
- (D)浏览器向资源服务器发出请求,其中不包括上一步收到的 Hash 值。
- (E)资源服务器返回一个网页,(其中包含的代码)可以获取 Hash 值中的令牌。
- (F)浏览器执行上一步获得的脚本,提取出令牌。
- (G)浏览器将令牌发给客户端。

客户端模式
步骤:
- (A)客户端向授权服务器进行身份认证,并要求一个访问令牌。
- (B)授权服务器确认无误后,向客户端提供访问令牌。

合并服务器
在项目比较小时,考虑到节省服务器资源,会考虑将授权服务器和资源服务器合并到一个项目中,避免启动多个 Java 进程。
Refresh Token
在 OAuth2.0 中,一共有两类令牌:
- 访问令牌(Access Token)
- 刷新令牌(Refresh Token):在访问令牌过期时,可以使用刷新令牌向授权服务器获取一个新的访问令牌。
为什么会有刷新令牌呢?
-
每次请求资源服务器时,都会在请求上带上访问令牌,这样它的泄露风险是相对高的。
- 因此,出于安全性的考虑,访问令牌的过期时间比较短,刷新令牌的过期时间比较长。
- 这样,如果访问令牌即使被盗用走,那么在一定的时间后,访问令牌也能在较短的时间吼过期。
- 当然,安全也是相对的,如果使用刷新令牌后,获取到新的访问令牌,访问令牌后续又可能被盗用。
常用开放平台的令牌过期时间:
| 开放平台 | Access Token 有效期 | Refresh Token 有效期 |
|---|---|---|
| 微信开放平台 | 2 小时 | 未知 |
| 腾讯开放平台 | 90 天 | 未知 |
| 小米开放平台 | 90 天 | 10 年 |
删除令牌
在用户登出系统时,会有删除令牌的需求。
- 虽然说,可以通过客户端本地删除令牌的方式实现。如,清除本地 Cookie、localStorage 的令牌缓存。
- 但是,考虑到真正的彻底的实现删除令牌,必然服务端自身需要删除令牌。
在 Spring Security OAuth2 中,并没有提供内置的接口,所以需要自己去实现。
- 具体的实现,通过调用 ConsumerTokenServices 的
#revokeToken(String tokenValue)方法,删除访问令牌和刷新令牌。 - 参看 《Spring Security OAuth2 – Simple Token Revocation》 文档,实现删除令牌的 API 接口。