“ 摘要: 原創(chuàng)出處 http://www./Spring-Security/OAuth2-learning/ 「芋道源碼」歡迎轉(zhuǎn)載,保留摘要,,謝謝,!
“ 本文在提供完整代碼示例,可見 https://github.com/YunaiV/SpringBoot-Labs 的 lab-68-spring-security-oauth 目錄,。
原創(chuàng)不易,,給點個 Star 嘿,一起沖鴨,!
1. 概述 在《芋道 Spring Boot 安全框架 Spring Security 入門》文章中,,艿艿分享了如何使用 Spring Security 實現(xiàn)認(rèn)證與授權(quán)的功能,獲得廣大女粉絲的好評,。
于是乎,,艿艿準(zhǔn)備再來分享一波 Spring Security OAuth 框架,看看在 Spring Security 如何實現(xiàn) OAuth2.0 實現(xiàn)授權(quán) 的功能,。
“ 旁白君:實際上艿艿很早寫了一篇關(guān)于 Spring Security OAuth 的文章,,考慮到版本太老,,提供的示例又過于簡單,所以本文也是該文章的升級版,。
可能有胖友對 OAuth2.0 不是很了解,,所以我們先來簡單介紹下它??赡芘钟芽?OAuth2.0 的概念會有點懵逼,,不要擔(dān)心,后續(xù)看完艿艿提供的示例代碼,,會突然清晰的哈,。
另外,阮一峰提供了幾篇關(guān)于 OAuth2.0 非常不錯的文章,,推薦胖友去從瞅瞅,。同時,本文也會直接引用它的內(nèi)容,,方便胖友統(tǒng)一理解,。
1.1 OAuth2.0 是什么? “ FROM 《維基百科 —— 開放授權(quán)》
OAuth(Open Authorization)是一個開放標(biāo)準(zhǔn),,允許用戶讓第三方應(yīng)用訪問該用戶在某一網(wǎng)站上存儲的私密的資源(如照片,,視頻,聯(lián)系人列表),,而無需將用戶名和密碼提供給第三方應(yīng)用 ,。
“ 旁白君:很多團隊,內(nèi)部會采用 OAuth2.0 實現(xiàn)一個授權(quán) 服務(wù),,避免每個上層應(yīng)用或者服務(wù)重復(fù)開發(fā),。
OAuth 允許用戶提供一個令牌 ,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù),。
每一個令牌授權(quán)一個特定的網(wǎng)站(例如,視頻編輯網(wǎng)站)在特定的時段(例如,,接下來的 2 小時內(nèi))內(nèi)訪問特定的資源 (例如僅僅是某一相冊中的視頻),。這樣,OAuth 讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲在另外服務(wù)提供者的某些特定信息,,而非所有內(nèi)容,。
“ 旁白君:如果胖友對接過微信網(wǎng)頁授權(quán)功能,就會發(fā)現(xiàn)分成兩種方式:靜默 授權(quán),、手動 授權(quán),。前者只能獲取到用戶的 openid ,而后者可以獲取到用戶的基本信息 ,。
OAuth2.0 是用于授權(quán)的行業(yè)標(biāo)準(zhǔn)協(xié)議 ,。OAuth2.0 為簡化客戶端開發(fā)提供了特定的授權(quán)流,,包括 Web 應(yīng)用、桌面應(yīng)用,、移動端應(yīng)用等,。
“ 旁白君:OAuth 1.0 協(xié)議體系本身存在一些問題,現(xiàn)已被各大開發(fā)平臺逐漸廢棄,。
1.2 OAuth2.0 角色解釋 在 OAuth2.0 中,,有如下角色:
① Authorization Server:認(rèn)證 服務(wù)器,用于認(rèn)證用戶,。如果客戶端認(rèn)證通過,,則發(fā)放訪問資源服務(wù)器的令牌 。
② Resource Server:資源 服務(wù)器,,擁有受保護資源,。如果請求包含正確的訪問令牌 ,則可以訪問資源,。
“ 友情提示:提供管理后臺,、客戶端 API 的服務(wù),都可以認(rèn)為是 Resource Server,。
③ Client :客戶端,。它請求資源服務(wù)器時,會帶上訪問令牌 ,,從而成功訪問資源,。
“ 友情提示:Client 可以是瀏覽器、客戶端,,也可以是內(nèi)部服務(wù),。
④ Resource Owner :資源擁有者。最終用戶,,他有訪問資源的賬號 與密碼 ,。
“ 友情提示:可以簡單把 Resource Owner 理解成人,她在使用 Client 訪問資源,。
1.3 OAuth 2.0 運行流程 如下是 OAuth 2.0 的授權(quán)碼模式 的運行流程:
OAuth 2.0 運行流程 “ (A)用戶打開客戶端以后,,客戶端要求用戶給予授權(quán)。 (C)客戶端使用上一步獲得的授權(quán),,向認(rèn)證服務(wù)器申請令牌,。 (D)認(rèn)證服務(wù)器對客戶端進行認(rèn)證以后,,確認(rèn)無誤,同意發(fā)放令牌,。 (E)客戶端使用令牌,,向資源服務(wù)器申請獲取資源,。 (F)資源服務(wù)器確認(rèn)令牌無誤,,同意向客戶端開放資源,。 上述的六個步驟,B 是關(guān)鍵 ,,即用戶如何給客戶端進行授權(quán) 。有了授權(quán)之,,客戶端就可以獲取令牌 ,,進而憑令牌獲取資源 。
“ 友情提示:如果胖友有對接過三方開放平臺,,例如說微信、QQ,、微博等三方登錄,,就會很容易理解這個步驟過程。
這個時候的資源,,資源主要指的是三方開放平臺的用戶資料等等,。
1.4 OAuth 2.0 授權(quán)模式 客戶端必須得到用戶的授權(quán)(Authorization Grant),才能獲得訪問令牌(Access Token),。
OAuth2.0 定義了四種授權(quán)方式:
授權(quán)碼模式(Authorization Code) 密碼模式(Resource Owner Password Credentials) 客戶端模式(Client Credentials) 其中,,密碼模式 和授權(quán)碼模式 比較常用。至于如何選擇,,艿艿這里先提前劇透下,,后續(xù)慢慢細(xì)品。
“ FROM 《深度剖析 OAuth2 和微服務(wù)安全架構(gòu)》
授權(quán)類型選擇 當(dāng)然,,對于黃框 部分,,對于筆者還是比較困惑的。筆者認(rèn)為,,第三方的單頁應(yīng)用 SPA ,也是適合采用 Authorization Code Grant 授權(quán)模式的,。例如,,《微信網(wǎng)頁授權(quán)》 :
“ 具體而言,網(wǎng)頁授權(quán)流程分為四步:
1,、引導(dǎo)用戶進入授權(quán)頁面同意授權(quán),,獲取 code 2,、通過 code 換取網(wǎng)頁授權(quán) access_token(與基礎(chǔ)支持中的 access_toke n不同) 3、如果需要,,開發(fā)者可以刷新網(wǎng)頁授權(quán) access_token,,避免過期 4、通過網(wǎng)頁授權(quán) access_token 和 openid 獲取用戶基本信息(支持 UnionID 機制) 所以,,艿艿猜測,,之所以圖中畫的是 Implicit Grant 的原因是,受 Google 的 《OAuth 2.0 for Client-side Web Applications》 一文中,,推薦使用了 Implicit Grant ,。
當(dāng)然,具體使用 Implicit Grant 還是 Authorization Code Grant 授權(quán)模式,,沒有定論,。筆者,偏向于使用 Authorization Code Grant ,,對于第三方客戶端的場景,。
2. 密碼模式 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-resource-owner-password-credentials
資源服務(wù)器:lab-68-demo02-resource-server
本小節(jié),我們來學(xué)習(xí)密碼模式(Resource Owner Password Credentials Grant) ,。
密碼模式,,用戶向客戶端提供自己的用戶名和密碼 ??蛻舳耸褂眠@些信息,,向授權(quán)服務(wù)器 索要授權(quán)。
在這種模式中,,用戶必須把自己的密碼給客戶端,,但是客戶端不得儲存密碼。這通常用在用戶對客戶端高度信任的情況下,,比如客戶端是操作系統(tǒng)的一部分,,或者由一個著名公司出品。而授權(quán)服務(wù)器只有在其他授權(quán)模式無法執(zhí)行的情況下,,才能考慮使用這種模式,。
“ 旁白君:如果客戶端和授權(quán)服務(wù)器都是自己公司的,顯然符合,。
密碼模式 “ (B)客戶端將用戶名和密碼 發(fā)給授權(quán)服務(wù)器,向后者請求令牌 ,。 (C)授權(quán)服務(wù)器確認(rèn)無誤后,,向客戶端提供訪問令牌。 下面,我們來新建兩個項目,,搭建一個密碼模式的使用示例,。如下圖所示:
項目結(jié)構(gòu) lab-68-demo02-authorization-server-with-resource-owner-password-credentials
:授權(quán)服務(wù)器。lab-68-demo02-resource-server
:資源服務(wù)器,。2.1 搭建授權(quán)服務(wù)器 創(chuàng)建 lab-68-demo02-authorization-server-with-resource-owner-password-credentials
項目,,搭建授權(quán)服務(wù)器。
2.1.1 引入依賴 創(chuàng)建 pom.xml
文件,,引入 Spring Security OAuth 依賴,。
<?xml version='1.0' encoding='UTF-8'?> <project xmlns ='http://maven./POM/4.0.0' xmlns:xsi ='http://www./2001/XMLSchema-instance' xsi:schemaLocation ='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd' > <parent > <artifactId > lab-68</artifactId > <groupId > cn.iocoder.springboot.labs</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > lab-68-demo02-authorization-server-with-resource-owner-password-credentials</artifactId > <properties > <!-- 依賴相關(guān)配置 --> <spring.boot.version > 2.2.4.RELEASE</spring.boot.version > <!-- 插件相關(guān)配置 --> <maven.compiler.target > 1.8</maven.compiler.target > <maven.compiler.source > 1.8</maven.compiler.source > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > ${spring.boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <!-- 實現(xiàn)對 Spring MVC 的自動配置 --> <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <!-- 實現(xiàn)對 Spring Security OAuth2 的自動配置 --> <dependency > <groupId > org.springframework.security.oauth.boot</groupId > <artifactId > spring-security-oauth2-autoconfigure</artifactId > <version > ${spring.boot.version}</version > </dependency > </dependencies > </project >
添加 spring-security-oauth2-autoconfigure
依賴,引入 Spring Security OAuth 并實現(xiàn)自動配置,。同時,,它也引入了 Spring Security 依賴。如下圖所示:
spring-security-oauth2-autoconfigure
2.1.2 SecurityConfig 創(chuàng)建 SecurityConfig 配置類,,提供一個賬號密碼為「yunai/1024」的用戶,。代碼如下:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean (name = BeanIds.AUTHENTICATION_MANAGER) public AuthenticationManager authenticationManagerBean () throws Exception { return super .authenticationManagerBean(); } @Bean public static NoOpPasswordEncoder passwordEncoder () { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth. // 使用內(nèi)存中的 InMemoryUserDetailsManager inMemoryAuthentication() // 不使用 PasswordEncoder 密碼編碼器 .passwordEncoder(passwordEncoder()) // 配置 yunai 用戶 .withUser('yunai' ).password('1024' ).roles('USER' ); } }
我們通過 Spring Security 提供認(rèn)證功能 ,所以這里需要配置一個用戶,。
“ 友情提示:看不懂這個配置的胖友,,后續(xù)可回《芋道 Spring Boot 安全框架 Spring Security 入門》重造下。
2.1.3 OAuth2AuthorizationServerConfig 創(chuàng)建 OAuth2AuthorizationServerConfig 配置類,,進行授權(quán) 服務(wù)器,。代碼如下:
@Configuration @EnableAuthorizationServer public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { /** * 用戶認(rèn)證 Manager */ @Autowired private AuthenticationManager authenticationManager; @Override public void configure (AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager); } @Override public void configure (AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.checkTokenAccess('isAuthenticated()' ); } @Override public void configure (ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // <4.1> .withClient('clientapp' ).secret('112233' ) // <4.2> Client 賬號、密碼,。 .authorizedGrantTypes('password' ) // <4.2> 密碼模式 .scopes('read_userinfo' , 'read_contacts' ) // <4.2> 可授權(quán)的 Scope // .and().withClient() // <4.3> 可以繼續(xù)配置新的 Client ; } }
① 在類上添加 @EnableAuthorizationServer
注解,,聲明開啟 OAuth 授權(quán) 服務(wù)器的功能。
同時,,繼承 AuthorizationServerConfigurerAdapter 類,,進行 OAuth 授權(quán) 服務(wù)器的配置。
② #configure(AuthorizationServerEndpointsConfigurer endpoints)
方法,,配置使用的 AuthenticationManager 實現(xiàn)用戶認(rèn)證 的功能,。其中,authenticationManager
是由「2.1.2 SecurityConfig」創(chuàng)建,,Spring Security 的配置類,。
③ #configure(AuthorizationServerSecurityConfigurer oauthServer)
方法,設(shè)置 /oauth/check_token
端點,,通過認(rèn)證后可訪問,。
“ 友情提示:這里的認(rèn)證,指的是使用 client-id
+ client-secret
進行的客戶端 認(rèn)證,,不要和用戶 認(rèn)證混淆,。
其中,,/oauth/check_token
端點對應(yīng) CheckTokenEndpoint 類,用于校驗訪問令牌的有效性,。
在客戶端訪問資源服務(wù)器時,會在請求中帶上訪問令牌 ,。 在資源服務(wù)器收到客戶端的請求時,,會使用請求中的訪問令牌 ,找授權(quán)服務(wù)器確認(rèn)該訪問令牌 的有效性,。 CheckTokenEndpoint 類 ④ #configure(ClientDetailsServiceConfigurer clients)
方法,,進行 Client 客戶端的配置。
<4.1>
處,,設(shè)置使用基于內(nèi)存 的 Client 存儲器,。實際情況下,最好放入數(shù)據(jù)庫 中,,方便管理,。
ClientDetailsService 子類 <4.2>
處,創(chuàng)建一個 Client 配置,。如果要繼續(xù)添加另外的 Client 配置,,可以在 <4.3>
處使用 #and()
方法繼續(xù)拼接。注意,,這里的 .withClient('clientapp').secret('112233')
代碼段,,就是 client-id
和 client-secret
。
“ 補充知識:可能會有胖友會問,,為什么要創(chuàng)建 Client 的 client-id
和 client-secret
呢,?
通過 client-id
編號和 client-secret
,授權(quán)服務(wù)器可以知道調(diào)用的來源以及正確性,。這樣,,即使“壞人”拿到 Access Token ,但是沒有 client-id
編號和 client-secret
,,也不能和授權(quán)服務(wù)器發(fā)生有效 的交互,。
2.1.4 AuthorizationServerApplication 創(chuàng)建 AuthorizationServerApplication 類,授權(quán)服務(wù)器的啟動類,。代碼如下:
@SpringBootApplication public class AuthorizationServerApplication { public static void main (String[] args) { SpringApplication.run(AuthorizationServerApplication.class, args); } }
2.1.5 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器,。下面,我們使用 Postman 模擬一個 Client ,。
① POST
請求 http://localhost:8080/oauth/token 地址,,使用密碼模式進行授權(quán) 。如下圖所示:
client-id
+ client-secret
進行 Client 認(rèn)證密碼模式的認(rèn)證 請求說明:
通過 Basic Auth 的方式,,填寫 client-id
+ client-secret
作為用戶名與密碼,,實現(xiàn) Client 客戶端有效性的認(rèn)證,。 請求參數(shù) grant_type
為 'password'
,表示使用密碼模式 ,。 請求參數(shù) username
和 password
,,表示用戶 的用戶名與密碼。 響應(yīng)說明:
響應(yīng)字段 access_token
為訪問令牌 ,,后續(xù)客戶端在訪問資源服務(wù)器時,,通過它作為身份的標(biāo)識。 響應(yīng)字段 token_type
為令牌類型 ,,一般是 bearer
或是 mac
類型,。 響應(yīng)字段 expires_in
為訪問令牌的過期時間 ,單位為秒,。 響應(yīng)字段 scope
為權(quán)限范圍 ,。 “ 友情提示:/oauth/token
對應(yīng) TokenEndpoint 端點,提供 OAuth2.0 的四種授權(quán)模式,。感興趣的胖友,,可以后續(xù)去擼擼。
② POST
請求 http://localhost:8080/oauth/check_token 地址,,校驗訪問令牌的有效性,。如下圖所示:
client-id
+ client-secret
進行 Client 認(rèn)證密碼模式的認(rèn)證 請求和響應(yīng)比較簡單,胖友自己瞅瞅即可,。
2.2 搭建資源服務(wù)器 創(chuàng)建 lab-68-demo02-resource-server
項目,,搭建資源服務(wù)器。
2.2.1 引入依賴 創(chuàng)建 pom.xml
文件,,引入 Spring Security OAuth 依賴,。
<?xml version='1.0' encoding='UTF-8'?> <project xmlns ='http://maven./POM/4.0.0' xmlns:xsi ='http://www./2001/XMLSchema-instance' xsi:schemaLocation ='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd' > <parent > <artifactId > lab-68</artifactId > <groupId > cn.iocoder.springboot.labs</groupId > <version > 1.0-SNAPSHOT</version > </parent > <modelVersion > 4.0.0</modelVersion > <artifactId > lab-68-demo02-resource-server</artifactId > <properties > <!-- 依賴相關(guān)配置 --> <spring.boot.version > 2.2.4.RELEASE</spring.boot.version > <!-- 插件相關(guān)配置 --> <maven.compiler.target > 1.8</maven.compiler.target > <maven.compiler.source > 1.8</maven.compiler.source > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > ${spring.boot.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <!-- 實現(xiàn)對 Spring MVC 的自動配置 --> <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <!-- 實現(xiàn)對 Spring Security OAuth2 的自動配置 --> <dependency > <groupId > org.springframework.security.oauth.boot</groupId > <artifactId > spring-security-oauth2-autoconfigure</artifactId > <version > ${spring.boot.version}</version > </dependency > </dependencies > </project >
“ 友情提示:和「2.1.1 引入依賴」小節(jié),是一致的哈,。
2.2.2 配置文件 創(chuàng)建 application.yml
配置文件,,添加 Spring Security OAuth 相關(guān)配置。
server: port: 9090 security: oauth2: # OAuth2 Client 配置,,對應(yīng) OAuth2ClientProperties 類 client: client-id: clientapp client-secret: 112233 # OAuth2 Resource 配置,,對應(yīng) ResourceServerProperties 類 resource: token-info-uri: http://127.0.0.1:8080/oauth/check_token # 獲得 Token 信息的 URL # 訪問令牌獲取 URL,自定義的 access-token-uri: http://127.0.0.1:8080/oauth/token
① security.oauth2.client
配置項,,OAuth2 Client 配置,,對應(yīng) OAuth2ClientProperties 類。在這個配置項中,,我們添加了客戶端的 client-id
和 client-secret
,。
為什么要添加這個配置項呢?因為資源服務(wù)器會調(diào)用授權(quán)服務(wù)器的 /oauth/check_token
接口,,而考慮到安全性,,我們配置了該接口需要進過客戶端認(rèn)證 ,。
“ 友情提示:這里艿艿偷懶了,其實單獨 給資源服務(wù)器配置一個 Client 的 client-id
和 client-secret
,。我們可以把資源服務(wù)器理解成授權(quán)服務(wù)器的一個特殊的客戶端 ,。
② security.oauth2.resource
配置項,OAuth2 Resource 配置,,對應(yīng) ResourceServerProperties 類,。
這里,我們通過 token-info-uri
配置項,,設(shè)置使用授權(quán)服務(wù)器的 /oauth/check_token
接口,校驗訪問令牌的有效性,。
③ security.access-token-uri
配置項,,是我們自定義 的,設(shè)置授權(quán)服務(wù)器的 oauth/token
接口,,獲取訪問令牌,。因為稍后我們將在 LoginController 中,實現(xiàn)一個 /login
登錄接口,。
2.2.3 OAuth2ResourceServerConfig 創(chuàng)建 OAuth2ResourceServerConfig 類,,進行資源 服務(wù)器。代碼如下:
@Configuration @EnableResourceServer public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure (HttpSecurity http) throws Exception { http.authorizeRequests() // 設(shè)置 /login 無需權(quán)限訪問 .antMatchers('/login' ).permitAll() // 設(shè)置其它請求,,需要認(rèn)證后訪問 .anyRequest().authenticated() ; } }
① 在類上添加 @EnableResourceServer
注解,,聲明開啟 OAuth 資源 服務(wù)器的功能。
同時,,繼承 ResourceServerConfigurerAdapter 類,,進行 OAuth 資源 服務(wù)器的配置。
② #configure(HttpSecurity http)
方法,,設(shè)置 HTTP 權(quán)限,。這里,我們設(shè)置 /login
接口無需 權(quán)限訪問,,其它接口認(rèn)證 后可訪問,。
這樣,客戶端在訪問資源服務(wù)器時,,其請求中的訪問令牌 會被資源 服務(wù)器調(diào)用授權(quán) 服務(wù)器的 /oauth/check_token
接口,,進行校驗訪問令牌的正確性。
2.2.4 ExampleController 創(chuàng)建 ExampleController 類,,提供 /api/example/hello
接口,,表示一個資源。代碼如下:
@RestController @RequestMapping ('/api/example' )public class ExampleController { @RequestMapping ('/hello' ) public String hello () { return 'world' ; } }
2.2.5 ResourceServerApplication 創(chuàng)建 ResourceServerApplication 類,,資源服務(wù)器的啟動類,。代碼如下:
@SpringBootApplication public class ResourceServerApplication { public static void main (String[] args) { SpringApplication.run(ResourceServerApplication.class, args); } }
2.2.6 簡單測試(第一彈) 執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器,。下面,我們來請求服務(wù)器的 <127.0.0.1:9090/api/example/hello> 接口,,進行相應(yīng)的測試,。
① 首先,請求 <127.0.0.1:9090/api/example/hello> 接口,,不帶 訪問令牌,,則請求會被攔截 。如下圖所示:
不帶訪問令牌 ② 然后,,請求 <127.0.0.1:9090/api/example/hello> 接口,,帶上錯誤 的訪問令牌,則請求會被攔截 ,。如下圖所示:
錯誤的訪問令牌 “ 友情提示:訪問令牌需要在請求頭 'Authorization'
上設(shè)置,,并且以 'Bearer '
開頭。
③ 最后,,請求 <127.0.0.1:9090/api/example/hello> 接口,,帶上正確 的訪問令牌,則請求會被通過 ,。如下圖所示:
正確的訪問令牌 2.2.7 LoginController 創(chuàng)建 LoginController 類,,提供 /login
登錄接口。代碼如下:
@RestController @RequestMapping ('/' )public class LoginController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value ('${security.oauth2.access-token-uri}' ) private String accessTokenUri; @PostMapping ('/login' ) public OAuth2AccessToken login (@RequestParam('username' ) String username, @RequestParam ('password' ) String password) { // <1> 創(chuàng)建 ResourceOwnerPasswordResourceDetails 對象 ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); resourceDetails.setUsername(username); resourceDetails.setPassword(password); // <2> 創(chuàng)建 OAuth2RestTemplate 對象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider()); // <3> 獲取訪問令牌 return restTemplate.getAccessToken(); } }
在 /login
接口中,,資源 服務(wù)器扮演的是一個 OAuth 客戶端 的角色,,調(diào)用授權(quán)服務(wù)器的 /oauth/token
接口,使用密碼模式 進行授權(quán),,獲得訪問令牌 ,。
① <1>
處,創(chuàng)建 ResourceOwnerPasswordResourceDetails 對象,,填寫密碼模式 授權(quán)需要的請求 參數(shù),。
② <2>
處,創(chuàng)建 OAuth2RestTemplate 對象,,它是 Spring Security OAuth 封裝的工具類,,用于請求授權(quán) 服務(wù)器。
同時,,將 ResourceOwnerPasswordAccessTokenProvider 設(shè)置到其中,,表示使用密碼模式 授權(quán)。
“ 友情提示:這一步非常重要,,艿艿在這里卡了非常非常非常久,,一度自閉要放棄。
③ <3>
處,,調(diào)用 OAuth2RestTemplate 的 #getAccessToken()
方法,,調(diào)用授權(quán)服務(wù)器的 /oauth/token
接口,,進行密碼 模式的授權(quán)。
注意,,OAuth2RestTemplate 是有狀態(tài) 的工具類,,所以需要每次都重新 創(chuàng)建。
2.2.8 簡單測試(第二彈) 重新執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器,。下面,,我們來進行 /login
接口的測試。
① 首先,,請求 http://127.0.0.1:9090/login 接口,,使用用戶 的用戶名 與密碼 進行登錄,獲得訪問令牌,。如下圖所示:
測試 /login
接口 響應(yīng)結(jié)果和授權(quán)服務(wù)器的 /oauth/token
接口是一致的,,因為就是調(diào)用它,嘿嘿~
② 然后,,請求 <127.0.0.1:9090/api/example/hello> 接口,帶剛剛的 訪問令牌,,則請求會被通過,。如下圖所示:
正確的訪問令牌 3. 授權(quán)碼模式 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-resource-owner-password-credentials
資源服務(wù)器:lab-68-demo02-resource-server
本小節(jié),我們來學(xué)習(xí)授權(quán)碼模式(Authorization Code) ,。
授權(quán)碼模式,,是功能最完整、流程最嚴(yán)密的授權(quán)模式,。它的特點就是通過客戶端的后臺 服務(wù)器,,與授權(quán) 務(wù)器進行互動。
“ 旁白君:一般情況下,,在有客戶端 的情況下,,我們與第三方平臺常常采用這種方式。
授權(quán)碼模式 “ (A)用戶訪問客戶端,,后者將前者跳轉(zhuǎn)到到授權(quán) 服務(wù)器,。 (C)假設(shè)用戶給予授權(quán),,授權(quán)服務(wù)器將跳轉(zhuǎn)到客戶端事先指定的'重定向 URI'(Redirection URI),,同時附上一個授權(quán)碼 。 (D)客戶端收到授權(quán)碼,,附上早先的'重定向 URI',,向認(rèn)證服務(wù)器申請令牌 。這一步是在客戶端的后臺的服務(wù)器上完成的,,對用戶不可見,。 (E)認(rèn)證服務(wù)器核對了授權(quán)碼 和重定向 URI ,,確認(rèn)無誤后,向客戶端發(fā)送訪問令牌 ,。 下面,,我們來新建兩個項目,搭建一個授權(quán)碼模式的使用示例,。如下圖所示:
項目結(jié)構(gòu) lab-68-demo02-authorization-server-with-resource-owner-password-credentials
:授權(quán)服務(wù)器,。lab-68-demo02-resource-server
:資源服務(wù)器。3.1 搭建授權(quán)服務(wù)器 復(fù)制出 lab-68-demo02-authorization-server-with-resource-owner-password-credentials
項目,,修改 搭建授權(quán)服務(wù)器,。改動點如下圖所示:
項目改動點 僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'authorization_code'
授權(quán)碼模式,,并設(shè)置回調(diào)地址,。
?? 注意,這里設(shè)置的回調(diào)地址,,稍后我們會在「3.2 搭建資源服務(wù)器」中實現(xiàn),。
3.1.1 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。
① 使用瀏覽器 ,,訪問 http://127.0.0.1:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://127.0.0.1:9090/callback&response_type=code&scope=read_userinfo 地址,,獲取授權(quán) 。請求參數(shù)說明如下:
client_id
參數(shù),,必傳 ,,為我們在 OAuth2AuthorizationServer 中配置的 Client 的編號。redirect_uri
參數(shù),,可選 ,,回調(diào)地址。當(dāng)然,,如果 client_id
對應(yīng)的 Client 未配置 redirectUris
屬性,,會報錯。response_type
參數(shù),,必傳 ,,返回結(jié)果為 code
授權(quán)碼 。scope
參數(shù),,可選 ,,申請授權(quán)的 Scope 。如果多個,,使用逗號分隔,。state
參數(shù),可選 ,表示客戶端的當(dāng)前狀態(tài),,可以指定任意值,,授權(quán)服務(wù)器會原封不動地返回這個值。“ 友情提示:state
參數(shù),,未在上述 URL 中體現(xiàn)出來,。
因為我們并未登錄 授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面,。如下圖所示:
登錄界面 ② 輸入用戶的賬號密碼「yunai/1024」進行登錄,。登錄完成后,進入授權(quán)界面,。如下圖所示:
“ 旁白君:和我們?nèi)粘J褂玫尿v訊 QQ,、微信、微博等等三方登錄,,是一模一樣的,,除了丑了點,嘿嘿~
授權(quán)界面 ③ 選擇 scope.read_userinfo
為 Approve 允許,,點擊「Authorize」按鈕,,完成授權(quán) 操作。瀏覽器自動重定向到 Redirection URI 地址,,并且在 URI 上可以看到 code
授權(quán)碼,。如下圖所示:
回調(diào)界面 “ 友情提示:/oauth/authorize
對應(yīng) AuthorizationEndpoint 端點。
④ 因為我們暫時沒有啟動資源 服務(wù)器,,所以顯示無法訪問。這里,,我們先使用 Postman 模擬請求 http://localhost:8080/oauth/token 地址,,使用授權(quán)碼 獲取到訪問令牌 。如下圖所示:
client-id
+ client-secret
進行 Client 認(rèn)證授權(quán)碼模式的認(rèn)證 請求說明:
通過 Basic Auth 的方式,,填寫 client-id
+ client-secret
作為用戶名與密碼,,實現(xiàn) Client 客戶端有效性的認(rèn)證。 請求參數(shù) grant_type
為 'authorization_code'
,,表示使用授權(quán)碼模式 ,。 請求參數(shù) code
,從授權(quán)服務(wù)器獲取到的授權(quán)碼 ,。 請求參數(shù) redirect_uri
,,Client 客戶端的 Redirection URI 地址。 注意,,授權(quán)碼僅能使用一次 ,,重復(fù)請求會報 Invalid authorization code:
錯誤。如下圖所示:
授權(quán)碼模式的認(rèn)證 - 失敗 3.2 搭建資源服務(wù)器 復(fù)用 lab-68-demo02-resource-server
項目,,主要是提供回調(diào)地址,。如下圖所示:
項目改動點 ① 新建 CallbackController 類,,提供 /callback
回調(diào)地址。
② 在 OAuth2ResourceServerConfig 配置類中,,設(shè)置 /callback
回調(diào)地址無需權(quán)限驗證,,不然回調(diào)都跳轉(zhuǎn)不過來哈。
3.2.1 CallbackController 創(chuàng)建 CallbackController 類,,提供 /callback
回調(diào)地址,,在獲取到授權(quán)碼 時,請求授權(quán) 服務(wù)器,,通過授權(quán)碼 獲取訪問令牌 ,。代碼如下:
@RestController @RequestMapping ('/' )public class CallbackController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value ('${security.oauth2.access-token-uri}' ) private String accessTokenUri; @GetMapping ('/callback' ) public OAuth2AccessToken login (@RequestParam('code' ) String code) { // 創(chuàng)建 AuthorizationCodeResourceDetails 對象 AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); // 創(chuàng)建 OAuth2RestTemplate 對象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code); // <1> 設(shè)置 code restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState('http://127.0.0.1:9090/callback' ); // <2> 通過這個方式,設(shè)置 redirect_uri 參數(shù) restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider()); // 獲取訪問令牌 return restTemplate.getAccessToken(); } }
代碼比較簡單,,還是使用 OAuth2RestTemplate 進行請求授權(quán)服務(wù)器,,胖友自己瞅瞅哈。
需要注意的是 <1>
和 <2>
處,,設(shè)置請求授權(quán)服務(wù)器需要的 code
和 redirect_uri
參數(shù),。
3.2.2 簡單測試 執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。
重復(fù)「3.2.1 簡單測試」的過程,,成功獲取到訪問令牌 ,。如下圖所示:
授權(quán)碼模式的認(rèn)證 - 成功 4. 簡化模式 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-implicit
資源服務(wù)器:lab-68-demo02-resource-server
本小節(jié),我們來學(xué)習(xí)簡化模式(Implicit) ,。
簡化模式,,不通過第三方應(yīng)用程序的服務(wù)器,直接在瀏覽器中向授權(quán) 服務(wù)器申請令牌,,跳過 了“授權(quán)碼”這個步驟,,因此得名。所有步驟在瀏覽器中完成,,令牌對訪問者是可見 的,且客戶端不需要授權(quán),。
簡化模式 “ (A)用戶訪問客戶端,,后者將前者跳轉(zhuǎn)到到授權(quán) 服務(wù)器。 (C)假設(shè)用戶給予授權(quán),,授權(quán)服務(wù)器將用戶導(dǎo)向客戶端指定的'重定向URI',并在 URI 的 Hash 部分 包含了訪問令牌 ,。 (D)瀏覽器向資源服務(wù)器發(fā)出請求,,其中不包括上一步收到的 Hash 值。 (E)資源服務(wù)器返回一個網(wǎng)頁,其中包含的代碼可以獲取 Hash 值中的令牌,。 (F)瀏覽器執(zhí)行上一步獲得的腳本,,提取出令牌。 項目結(jié)構(gòu) lab-68-demo02-authorization-server-with-implicit
:授權(quán)服務(wù)器,。lab-68-demo02-resource-server
:資源服務(wù)器。4.1 搭建授權(quán)服務(wù)器 復(fù)制出 lab-68-demo02-authorization-server-with-implicit
項目,,修改 搭建授權(quán)服務(wù)器,。改動點如下圖所示:
項目改動點 僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'implicit'
簡化模式,,并設(shè)置回調(diào)地址,。
?? 注意,這里設(shè)置的回調(diào)地址,,稍后我們會在「4.2 搭建資源服務(wù)器」中實現(xiàn),。
4.2 搭建資源服務(wù)器 復(fù)用 lab-68-demo02-resource-server
項目,主要是提供回調(diào)地址,。如下圖所示:
項目改動點 ① 新建 Callback02Controller 類,,提供 /callback02
回調(diào)地址。代碼如下:
@RestController @RequestMapping ('/' )public class Callback02Controller { @GetMapping ('/callback02' ) public String login () { return '假裝這里有一個頁面' ; } }
“ 友情提示:考慮到暫時不想做頁面,,所以這里先假裝一下,,嘿嘿。
② 在 OAuth2ResourceServerConfig 配置類中,,設(shè)置 /callback02
回調(diào)地址無需權(quán)限驗證,,不然回調(diào)都跳轉(zhuǎn)不過來哈。
4.3 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器,。 執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器,。
① 使用瀏覽器 ,訪問 http://127.0.0.1:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://127.0.0.1:9090/callback02&response_type=token&scope=read_userinfo 地址,,獲取授權(quán) 。請求參數(shù)說明如下:
client_id
參數(shù),,必傳 ,,為我們在 OAuth2AuthorizationServer 中配置的 Client 的編號。redirect_uri
參數(shù),,可選 ,,回調(diào)地址。當(dāng)然,,如果 client_id
對應(yīng)的 Client 未配置 redirectUris
屬性,,會報錯。response_type
參數(shù),必傳 ,,返回結(jié)果為 token
訪問令牌 ,。scope
參數(shù),可選 ,,申請授權(quán)的 Scope ,。如果多個,使用逗號分隔,。state
參數(shù),,可選 ,表示客戶端的當(dāng)前狀態(tài),,可以指定任意值,,授權(quán)服務(wù)器會原封不動地返回這個值。“ 友情提示:state
參數(shù),,未在上述 URL 中體現(xiàn)出來,。
因為我們并未登錄 授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面,。如下圖所示:
登錄界面 ② 輸入用戶的賬號密碼「yunai/1024」進行登錄,。登錄完成后,進入授權(quán)界面,。如下圖所示:
“ 旁白君:和我們?nèi)粘J褂玫尿v訊 QQ,、微信、微博等等三方登錄,,是一模一樣的,,除了丑了點,嘿嘿~
授權(quán)界面 ③ 選擇 scope.read_userinfo
為 Approve 允許,,點擊「Authorize」按鈕,,完成授權(quán) 操作。瀏覽器自動重定向到 Redirection URI 地址,,并且在 URI 上的 Hash 部分 可以看到 access_token
訪問令牌,。如下圖所示:
回調(diào)界面 后續(xù),可以通過編寫 Javascript 腳本的代碼,,獲取 URI 上的 Hash 部分 的訪問令牌,。
5. 客戶端模式 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-client-credentials
資源服務(wù)器:lab-68-demo02-resource-server
本小節(jié),我們來學(xué)習(xí)客戶端模式(Client Credentials) ,。
客戶端模式,,指客戶端以自己的名義,而不是以用戶的名義,,向授權(quán)服務(wù)器進行認(rèn)證,。
嚴(yán)格地說,,客戶端模式并不屬于 OAuth 框架所要解決的問題。在這種模式中,,用戶直接向客戶端注冊,,客戶端以自己的名義要求授權(quán)服務(wù)器提供服務(wù),其實不存在授權(quán)問題,。
“ 旁白君:我們對接微信公眾號時,,就采用的客戶端模式。我們的后端服務(wù)器就扮演“客戶端”的角色,,與微信公眾號的后端服務(wù)器進行交互,。
客戶端模式 “ (A)客戶端向授權(quán)服務(wù)器進行身份認(rèn)證,并要求一個訪問令牌 ,。 (B)授權(quán)服務(wù)器確認(rèn)無誤后,,向客戶端提供訪問令牌。 下面,,我們來新建兩個項目,,搭建一個客戶端模式的使用示例。如下圖所示:
項目結(jié)構(gòu) lab-68-demo02-authorization-server-with-client-credentials
:授權(quán)服務(wù)器,。lab-68-demo02-resource-server
:資源服務(wù)器,。5.1 搭建授權(quán)服務(wù)器 復(fù)制出 lab-68-demo02-authorization-server-with-client-credentials
項目,修改 搭建授權(quán)服務(wù)器,。改動點如下圖所示:
項目改動點 ① 刪除 SecurityConfig 配置類,,因為客戶端模式下,無需 Spring Security 提供用戶的認(rèn)證功能,。
但是,,Spring Security OAuth 需要一個 PasswordEncoder Bean,否則會報錯,,因此我們在 OAuth2AuthorizationServerConfig 類的 #passwordEncoder()
方法進行創(chuàng)建,。
② 修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'client_credentials'
客戶端模式,。
5.1.1 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器,。下面,我們使用 Postman 模擬一個 Client ,。
① POST
請求 http://localhost:8080/oauth/token 地址,,使用客戶端模式進行授權(quán) 。如下圖所示:
client-id
+ client-secret
進行 Client 認(rèn)證客戶端模式的認(rèn)證 請求說明:
通過 Basic Auth 的方式,,填寫 client-id
+ client-secret
作為用戶名與密碼,實現(xiàn) Client 客戶端有效性的認(rèn)證,。 請求參數(shù) grant_type
為 'client_credentials'
,,表示使用客戶端模式 ,。 響應(yīng)就是訪問令牌,胖友自己瞅瞅即可,。
5.2 搭建資源服務(wù)器 復(fù)用 lab-68-demo02-resource-server
項目,,修改點如下圖所示:
項目改動點 ① 新建 ClientLoginController 類,提供 /client-login
接口,,實現(xiàn)調(diào)用授權(quán) 服務(wù)器,,進行客戶端 模式的授權(quán),獲得訪問令牌,。代碼如下:
@RestController @RequestMapping ('/' )public class ClientLoginController { @Autowired private OAuth2ClientProperties oauth2ClientProperties; @Value ('${security.oauth2.access-token-uri}' ) private String accessTokenUri; @PostMapping ('/client-login' ) public OAuth2AccessToken login () { // 創(chuàng)建 ClientCredentialsResourceDetails 對象 ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); resourceDetails.setAccessTokenUri(accessTokenUri); resourceDetails.setClientId(oauth2ClientProperties.getClientId()); resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret()); // 創(chuàng)建 OAuth2RestTemplate 對象 OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); restTemplate.setAccessTokenProvider(new ClientCredentialsAccessTokenProvider()); // 獲取訪問令牌 return restTemplate.getAccessToken(); } }
代碼比較簡單,,還是使用 OAuth2RestTemplate 進行請求授權(quán)服務(wù)器,胖友自己瞅瞅哈,。
② 在 OAuth2ResourceServerConfig 配置類中,,設(shè)置 /client-login
接口無需權(quán)限驗證,不然無法調(diào)用哈,。
5.2.1 簡單測試 執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器,。
① 使用「5.1.1 簡單測試」小節(jié)獲得的訪問令牌 ,請求 <127.0.0.1:9090/api/example/hello> 接口時帶上 ,,則請求會被通過 ,。如下圖所示:
正確的訪問令牌 ② 請求 http://127.0.0.1:9090/clientlogin 接口,使用客戶端模式 進行授權(quán),,獲得訪問令牌,。如下圖所示:
測試 client-login
接口 響應(yīng)結(jié)果和授權(quán)服務(wù)器的 /oauth/token
接口是一致的,因為就是調(diào)用它,,嘿嘿~
6. 合并服務(wù)器 “ 旁白君:這個小節(jié)的標(biāo)題,,艿艿有點不知道怎么取了,就先叫合并服務(wù)器吧 = =,!
在項目比較小時,,考慮到節(jié)省服務(wù)器資源,會考慮將授權(quán) 服務(wù)器和資源 服務(wù)器合并 到一個項目中,,避免啟動多個 Java 進程,。良心的艿艿,編寫了四種授權(quán)模式的示例,,如下圖所示:
示例項目 基于密碼 模式的示例:lab-68-demo01-resource-owner-password-credentials-server
基于授權(quán)碼 模式的示例:lab-68-demo01-authorization-code-server
基于簡化 模式的示例:lab-68-demo01-implicit-server
基于客戶端 模式的示例:lab-68-demo01-client-credentials-server
具體的代碼實現(xiàn),,實際和上述每個授權(quán)模式對應(yīng)的小節(jié)是基本一致的,只是說將代碼“放 ”在了一個項目中,。嘿嘿~
7. 刷新令牌 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials
在 OAuth2.0 中,,一共有兩類 令牌:
在訪問 令牌過期時,我們可以使用刷新 令牌向授權(quán) 服務(wù)器獲取一個新 的訪問令牌,。
可能會胖友有疑惑,,為什么會有刷新 令牌呢,?每次請求資源服務(wù)器時,都會在請求上帶上訪問 令牌,,這樣它的泄露風(fēng)險是相對 高的,。
因此,出于安全性 的考慮,,訪問令牌的過期時間比較短 ,,刷新令牌的過期時間比較長 。這樣,,如果訪問令牌即使被盜用走,,那么在一定的時間后,訪問令牌也能在較短的時間吼過期,。當(dāng)然,,安全也是相對的,如果使用刷新令牌后,,獲取到新的訪問令牌,,訪問令牌后續(xù) 又可能 被盜用。
艿艿整理了下,,大家常用開放平臺的令牌過期時間,,讓大家更好的理解:
開放平臺 Access Token 有效期 Refresh Token 有效期 微信開放平臺 2 小時 未知 騰訊開放平臺 90 天 未知 小米開放平臺 90 天 10 年
7.1 示例項目 下面,復(fù)制出 lab-68-demo03-authorization-server-with-client-credentials
項目,,搭建提供訪問令牌 的授權(quán) 服務(wù)器。改動點如下圖所示:
項目改動點 ① 在 OAuth2AuthorizationServerConfig 的 #configure(ClientDetailsServiceConfigurer clients)
方法中,,在配置的 Client 的授權(quán)模式中,,額外新增 'refresh_token'
刷新令牌。
通過 #accessTokenValiditySeconds(int accessTokenValiditySeconds)
方法,,設(shè)置訪問 令牌的有效期,。 通過 #refreshTokenValiditySeconds(int refreshTokenValiditySeconds)
方法,設(shè)置刷新 令牌的有效期,。
② 在 OAuth2AuthorizationServerConfig 的 #configure(AuthorizationServerEndpointsConfigurer endpoints)
方法中,,設(shè)置使用的 userDetailsService
用戶詳情 Service。
而該 userDetailsService
是在 SecurityConfig 的 #userDetailsServiceBean()
方法創(chuàng)建的 UserDetailsService Bean,。
“ 友情提示:如果不進行 UserDetailsService 的設(shè)置,,在使用刷新 令牌獲取新的訪問 令牌時,會拋出異常,。
7.2 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器,。下面,我們使用 Postman 模擬一個 Client ,。
① POST
請求 http://localhost:8080/oauth/token 地址,,使用密碼 模式進行授權(quán) ,。如下圖所示:
密碼模式的認(rèn)證 額外 多返回了 refresh_token
刷新令牌。
② POST
請求 http://localhost:8080/oauth/token 地址,,使用刷新令牌 模式進行授權(quán) 。如下圖所示:
刷新令牌模式的認(rèn)證 請求說明:
通過 Basic Auth 的方式,,填寫 client-id
+ client-secret
作為用戶名與密碼,實現(xiàn) Client 客戶端有效性的認(rèn)證,。 請求參數(shù) grant_type
為 'refresh_token'
,,表示使用刷新令牌模式 。 請求參數(shù) refresh_token
,,表示刷新令牌 ,。 在響應(yīng)中,,返回了新的 access_token
訪問 令牌。注意,,老的 access_token
訪問 令牌會失效 ,,無法繼續(xù)使用。
8. 刪除令牌 “ 示例代碼對應(yīng)倉庫:
授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials
在用戶登出 系統(tǒng)時,,我們會有刪除 令牌的需求,。雖然說,,可以通過客戶端本地 刪除令牌的方式實現(xiàn)。但是,,考慮到真正的徹底的實現(xiàn)刪除令牌,,必然服務(wù)端自身 需要刪除令牌。
“ 友情提示:客戶端本地 刪除令牌的方式實現(xiàn),,指的是清楚本地 Cookie,、localStorage 的令牌緩存。
在 Spring Security OAuth2 中,,并沒有提供內(nèi)置的接口 ,,所以需要自己去實現(xiàn)。筆者參看 《Spring Security OAuth2 – Simple Token Revocation》 文檔,,實現(xiàn)刪除令牌的 API 接口,。
具體的實現(xiàn),通過調(diào)用 ConsumerTokenServices 的 #revokeToken(String tokenValue)
方法,,刪除訪問 令牌和刷新 令牌,。如下圖所示:
ConsumerTokenServices 實現(xiàn)類 8.1 示例項目 下面,我們直接在授權(quán) 服務(wù)器 lab-68-demo03-authorization-server-with-resource-owner-password-credentials
項目,,修改接入刪除令牌的功能,。改動點如下圖所示:
項目改動點 ① 創(chuàng)建 TokenDemoController 類,提供 /token/demo/revoke
接口,,調(diào)用 ConsumerTokenServices 的 #revokeToken(String tokenValue)
方法,,刪除訪問 令牌和刷新 令牌。代碼如下:
@RestController @RequestMapping ('/token/demo' )public class TokenDemoController { @Autowired private ConsumerTokenServices tokenServices; @PostMapping (value = '/revoke' ) public boolean revokeToken (@RequestParam('token' ) String token) { return tokenServices.revokeToken(token); } }
② 在 SecurityConfig 配置類,,設(shè)置 /token/demo/revoke
接口無需授權(quán) ,,方便測試。代碼如下:
// SecurityConfig.java @Override protected void configure (HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() // 設(shè)置 /token/demo/revoke 無需授權(quán) .mvcMatchers('/token/demo/revoke' ).permitAll() // 設(shè)置其它接口需要授權(quán) .anyRequest().authenticated(); }
8.2 簡單測試 執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器,。下面,,我們使用 Postman 模擬一個 Client 。
① POST
請求 http://localhost:8080/oauth/token 地址,,使用密碼 模式進行授權(quán) ,。如下圖所示:
密碼模式的認(rèn)證 ② POST
請求 http://localhost:8080/token/demo/revoke 地址,刪除令牌,。如下圖所示:
刪除令牌 刪除成功,。后續(xù),胖友可以自己調(diào)用授權(quán) 服務(wù)器的 oauth/check_token
接口,,測試訪問 令牌是否已經(jīng)被刪除,。
666. 彩蛋 至此,我們完整學(xué)習(xí) Spring Security OAuth 框架,。不過 Spring 團隊宣布該框架處于 Deprecation 廢棄 狀態(tài),。如下圖所示:
Spring Security OAuth 被廢棄 同時,Spring 團隊正在實現(xiàn)新的 Spring Authorization Server 授權(quán) 服務(wù)器,,目前還處于 Experimental 實驗 狀態(tài),。
實際項目中,根據(jù)艿艿了解到的情況,,很少項目會直接采用 Spring Security OAuth 框架,,而是自己參考它進行 OAuth2.0 的實現(xiàn) 。并且,,一般只會實現(xiàn)密碼 授權(quán)模式,。
在本文中,我們采用基于內(nèi)存 的 InMemoryTokenStore,,實現(xiàn)訪問 令牌和刷新 令牌的存儲,。它會存在兩個明顯的缺點 :
重啟 授權(quán)服務(wù)器時,令牌信息會丟失 ,導(dǎo)致用戶需要重新授權(quán),。多個 授權(quán)服務(wù)器時,,令牌信息無法共享 ,導(dǎo)致用戶一會授權(quán)成功,,一會授權(quán)失敗,。因此,下一篇《芋道 Spring Security OAuth2 存儲器》文章,,我們來學(xué)習(xí) Spring Security OAuth 提供的基于數(shù)據(jù)庫 和 Redis 的存儲器,。走起~