日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

可能是第二好的 Spring OAuth 2.0 文章,艿艿端午在家写了 3 天~

發(fā)布時(shí)間:2025/3/21 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 可能是第二好的 Spring OAuth 2.0 文章,艿艿端午在家写了 3 天~ 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文在提供完整代碼示例,可見 https://github.com/YunaiV/SpringBoot-Labs 的 lab-68-spring-security-oauth 目錄。

原創(chuàng)不易,給點(diǎn)個(gè) Star 嘿,一起沖鴨!

1. 概述

在《芋道 Spring Boot 安全框架 Spring Security 入門》文章中,艿艿分享了如何使用 Spring Security 實(shí)現(xiàn)認(rèn)證與授權(quán)的功能,獲得廣大女粉絲的好評(píng)。

于是乎,艿艿準(zhǔn)備再來分享一波 Spring Security OAuth 框架,看看在 Spring Security 如何實(shí)現(xiàn) OAuth2.0 實(shí)現(xiàn)授權(quán)的功能。

旁白君:實(shí)際上艿艿很早寫了一篇關(guān)于 Spring Security OAuth 的文章,考慮到版本太老,提供的示例又過于簡(jiǎn)單,所以本文也是該文章的升級(jí)版。

可能有胖友對(duì) OAuth2.0 不是很了解,所以我們先來簡(jiǎn)單介紹下它。可能胖友看 OAuth2.0 的概念會(huì)有點(diǎn)懵逼,不要擔(dān)心,后續(xù)看完艿艿提供的示例代碼,會(huì)突然清晰的哈。

另外,阮一峰提供了幾篇關(guān)于 OAuth2.0 非常不錯(cuò)的文章,推薦胖友去從瞅瞅。同時(shí),本文也會(huì)直接引用它的內(nèi)容,方便胖友統(tǒng)一理解。

  • 《理解 OAuth2.0》

  • 《OAuth2.0 的一個(gè)簡(jiǎn)單解釋》

  • 《OAuth2.0 的四種方式》

  • 《GitHub OAuth 第三方登錄示例教程》

1.1 OAuth2.0 是什么?

FROM 《維基百科 —— 開放授權(quán)》

OAuth(Open Authorization)是一個(gè)開放標(biāo)準(zhǔn),允許用戶讓第三方應(yīng)用訪問該用戶在某一網(wǎng)站上存儲(chǔ)的私密的資源(如照片,視頻,聯(lián)系人列表),而無需將用戶名和密碼提供給第三方應(yīng)用

旁白君:很多團(tuán)隊(duì),內(nèi)部會(huì)采用 OAuth2.0 實(shí)現(xiàn)一個(gè)授權(quán)服務(wù),避免每個(gè)上層應(yīng)用或者服務(wù)重復(fù)開發(fā)。

OAuth 允許用戶提供一個(gè)令牌,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù)。

每一個(gè)令牌授權(quán)一個(gè)特定的網(wǎng)站(例如,視頻編輯網(wǎng)站)在特定的時(shí)段(例如,接下來的 2 小時(shí)內(nèi))內(nèi)訪問特定的資源(例如僅僅是某一相冊(cè)中的視頻)。這樣,OAuth 讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲(chǔ)在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容。

旁白君:如果胖友對(duì)接過微信網(wǎng)頁授權(quán)功能,就會(huì)發(fā)現(xiàn)分成兩種方式:靜默授權(quán)、手動(dòng)授權(quán)。前者只能獲取到用戶的?openid,而后者可以獲取到用戶的基本信息

OAuth2.0 是用于授權(quán)的行業(yè)標(biāo)準(zhǔn)協(xié)議。OAuth2.0 為簡(jiǎn)化客戶端開發(fā)提供了特定的授權(quán)流,包括 Web 應(yīng)用、桌面應(yīng)用、移動(dòng)端應(yīng)用等。

旁白君:OAuth 1.0 協(xié)議體系本身存在一些問題,現(xiàn)已被各大開發(fā)平臺(tái)逐漸廢棄。

1.2 OAuth2.0 角色解釋

在 OAuth2.0 中,有如下角色:

①?Authorization?Server:認(rèn)證服務(wù)器,用于認(rèn)證用戶。如果客戶端認(rèn)證通過,則發(fā)放訪問資源服務(wù)器的令牌

②?Resource?Server:資源服務(wù)器,擁有受保護(hù)資源。如果請(qǐng)求包含正確的訪問令牌,則可以訪問資源。

友情提示:提供管理后臺(tái)、客戶端 API 的服務(wù),都可以認(rèn)為是 Resource Server。

③?Client:客戶端。它請(qǐng)求資源服務(wù)器時(shí),會(huì)帶上訪問令牌,從而成功訪問資源。

友情提示:Client 可以是瀏覽器、客戶端,也可以是內(nèi)部服務(wù)。

④ Resource?Owner:資源擁有者。最終用戶,他有訪問資源的賬號(hào)密碼

友情提示:可以簡(jiǎn)單把 Resource Owner 理解成人,她在使用 Client 訪問資源。

1.3 OAuth 2.0 運(yùn)行流程

如下是 OAuth 2.0 的授權(quán)碼模式的運(yùn)行流程:

OAuth 2.0 運(yùn)行流程

?

  • (A)用戶打開客戶端以后,客戶端要求用戶給予授權(quán)。

  • (B)用戶同意給予客戶端授權(quán)。

  • (C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請(qǐng)令牌。

  • (D)認(rèn)證服務(wù)器對(duì)客戶端進(jìn)行認(rèn)證以后,確認(rèn)無誤,同意發(fā)放令牌。

  • (E)客戶端使用令牌,向資源服務(wù)器申請(qǐng)獲取資源。

  • (F)資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源。

上述的六個(gè)步驟,B 是關(guān)鍵,即用戶如何給客戶端進(jìn)行授權(quán)。有了授權(quán)之,客戶端就可以獲取令牌,進(jìn)而憑令牌獲取資源

友情提示:如果胖友有對(duì)接過三方開放平臺(tái),例如說微信、QQ、微博等三方登錄,就會(huì)很容易理解這個(gè)步驟過程。

這個(gè)時(shí)候的資源,資源主要指的是三方開放平臺(tái)的用戶資料等等。

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)

  • 簡(jiǎn)化模式(Implicit)

  • 客戶端模式(Client Credentials)

其中,密碼模式授權(quán)碼模式比較常用。至于如何選擇,艿艿這里先提前劇透下,后續(xù)慢慢細(xì)品。

FROM 《深度剖析 OAuth2 和微服務(wù)安全架構(gòu)》

授權(quán)類型選擇

?

當(dāng)然,對(duì)于黃框部分,對(duì)于筆者還是比較困惑的。筆者認(rèn)為,第三方的單頁應(yīng)用 SPA ,也是適合采用 Authorization Code Grant 授權(quán)模式的。例如,《微信網(wǎng)頁授權(quán)》 :

具體而言,網(wǎng)頁授權(quán)流程分為四步:

  • 1、引導(dǎo)用戶進(jìn)入授權(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 機(jī)制)

所以,艿艿猜測(cè),之所以圖中畫的是 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,對(duì)于第三方客戶端的場(chǎng)景。

2. 密碼模式

示例代碼對(duì)應(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)。

在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲(chǔ)存密碼。這通常用在用戶對(duì)客戶端高度信任的情況下,比如客戶端是操作系統(tǒng)的一部分,或者由一個(gè)著名公司出品。而授權(quán)服務(wù)器只有在其他授權(quán)模式無法執(zhí)行的情況下,才能考慮使用這種模式。

旁白君:如果客戶端和授權(quán)服務(wù)器都是自己公司的,顯然符合。

密碼模式

?

  • (A)用戶向客戶端提供用戶名和密碼。

  • (B)客戶端將用戶名和密碼發(fā)給授權(quán)服務(wù)器,向后者請(qǐng)求令牌

  • (C)授權(quán)服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

下面,我們來新建兩個(gè)項(xiàng)目,搭建一個(gè)密碼模式的使用示例。如下圖所示:

項(xiàng)目結(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?項(xiàng)目,搭建授權(quán)服務(wù)器。

2.1.1 引入依賴

創(chuàng)建?pom.xml?文件,引入 Spring Security OAuth 依賴。

<?xml?version="1.0"?encoding="UTF-8"?> <project?xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/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><!--?實(shí)現(xiàn)對(duì)?Spring?MVC?的自動(dòng)配置?--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--?實(shí)現(xiàn)對(duì)?Spring?Security?OAuth2?的自動(dòng)配置?--><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 并實(shí)現(xiàn)自動(dòng)配置。同時(shí),它也引入了 Spring Security 依賴。如下圖所示:

spring-security-oauth2-autoconfigure

?

2.1.2 SecurityConfig

創(chuàng)建 SecurityConfig 配置類,提供一個(gè)賬號(hào)密碼為「yunai/1024」的用戶。代碼如下:

@Configuration @EnableWebSecurity public?class?SecurityConfig?extends?WebSecurityConfigurerAdapter?{@Override@Bean(name?=?BeanIds.AUTHENTICATION_MANAGER)public?AuthenticationManager?authenticationManagerBean()?throws?Exception?{return?super.authenticationManagerBean();}@Beanpublic?static?NoOpPasswordEncoder?passwordEncoder()?{return?(NoOpPasswordEncoder)?NoOpPasswordEncoder.getInstance();}@Overrideprotected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{auth.//?使用內(nèi)存中的?InMemoryUserDetailsManagerinMemoryAuthentication()//?不使用?PasswordEncoder?密碼編碼器.passwordEncoder(passwordEncoder())//?配置?yunai?用戶.withUser("yunai").password("1024").roles("USER");}}

我們通過?Spring Security 提供認(rèn)證功能,所以這里需要配置一個(gè)用戶。

友情提示:看不懂這個(gè)配置的胖友,后續(xù)可回《芋道 Spring Boot 安全框架 Spring Security 入門》重造下。

2.1.3 OAuth2AuthorizationServerConfig

創(chuàng)建 OAuth2AuthorizationServerConfig 配置類,進(jìn)行授權(quán)服務(wù)器。代碼如下:

@Configuration @EnableAuthorizationServer public?class?OAuth2AuthorizationServerConfig?extends?AuthorizationServerConfigurerAdapter?{/***?用戶認(rèn)證?Manager*/@Autowiredprivate?AuthenticationManager?authenticationManager;@Overridepublic?void?configure(AuthorizationServerEndpointsConfigurer?endpoints)?throws?Exception?{endpoints.authenticationManager(authenticationManager);}@Overridepublic?void?configure(AuthorizationServerSecurityConfigurer?oauthServer)?throws?Exception?{oauthServer.checkTokenAccess("isAuthenticated()");}@Overridepublic?void?configure(ClientDetailsServiceConfigurer?clients)?throws?Exception?{clients.inMemory()?//?<4.1>.withClient("clientapp").secret("112233")?//?<4.2> Client 賬號(hào)、密碼。.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ù)器的功能。

同時(shí),繼承 AuthorizationServerConfigurerAdapter 類,進(jìn)行 OAuth?授權(quán)服務(wù)器的配置。

②?#configure(AuthorizationServerEndpointsConfigurer endpoints)?方法,配置使用的 AuthenticationManager 實(shí)現(xiàn)用戶認(rèn)證的功能。其中,authenticationManager?是由「2.1.2 SecurityConfig」創(chuàng)建,Spring Security 的配置類。

③?#configure(AuthorizationServerSecurityConfigurer oauthServer)?方法,設(shè)置?/oauth/check_token?端點(diǎn),通過認(rèn)證后可訪問。

友情提示:這里的認(rèn)證,指的是使用?client-id?+?client-secret?進(jìn)行的客戶端認(rèn)證,不要和用戶認(rèn)證混淆。

其中,/oauth/check_token?端點(diǎn)對(duì)應(yīng) CheckTokenEndpoint 類,用于校驗(yàn)訪問令牌的有效性。

  • 在客戶端訪問資源服務(wù)器時(shí),會(huì)在請(qǐng)求中帶上訪問令牌

  • 在資源服務(wù)器收到客戶端的請(qǐng)求時(shí),會(huì)使用請(qǐng)求中的訪問令牌,找授權(quán)服務(wù)器確認(rèn)該訪問令牌的有效性。

CheckTokenEndpoint 類

④?#configure(ClientDetailsServiceConfigurer clients)?方法,進(jìn)行 Client 客戶端的配置。

<4.1>?處,設(shè)置使用基于內(nèi)存的 Client 存儲(chǔ)器。實(shí)際情況下,最好放入數(shù)據(jù)庫中,方便管理。

ClientDetailsService 子類

<4.2>?處,創(chuàng)建一個(gè) Client 配置。如果要繼續(xù)添加另外的 Client 配置,可以在?<4.3>?處使用?#and()?方法繼續(xù)拼接。注意,這里的?.withClient("clientapp").secret("112233")?代碼段,就是?client-id?和?client-secret。

補(bǔ)充知識(shí):可能會(huì)有胖友會(huì)問,為什么要?jiǎng)?chuàng)建 Client 的?client-id?和?client-secret?呢?

通過?client-id?編號(hào)和?client-secret,授權(quán)服務(wù)器可以知道調(diào)用的來源以及正確性。這樣,即使“壞人”拿到 Access Token ,但是沒有?client-id?編號(hào)和?client-secret,也不能和授權(quán)服務(wù)器發(fā)生有效的交互。

2.1.4 AuthorizationServerApplication

創(chuàng)建 AuthorizationServerApplication 類,授權(quán)服務(wù)器的啟動(dòng)類。代碼如下:

@SpringBootApplication public?class?AuthorizationServerApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(AuthorizationServerApplication.class,?args);}}

2.1.5 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(quán)服務(wù)器。下面,我們使用?Postman 模擬一個(gè) Client

①?POST?請(qǐng)求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

?

  • client-id?+?client-secret?進(jìn)行 Client 認(rèn)證 密碼模式的認(rèn)證
  • 密碼模式的認(rèn)證

請(qǐng)求說明:

  • 通過 Basic Auth 的方式,填寫?client-id?+?client-secret?作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。

  • 請(qǐng)求參數(shù)?grant_type?為?"password",表示使用密碼模式

  • 請(qǐng)求參數(shù)?username?和?password,表示用戶的用戶名與密碼。

響應(yīng)說明:

  • 響應(yīng)字段?access_token?為訪問令牌,后續(xù)客戶端在訪問資源服務(wù)器時(shí),通過它作為身份的標(biāo)識(shí)。

  • 響應(yīng)字段?token_type?為令牌類型,一般是?bearer?或是?mac?類型。

  • 響應(yīng)字段?expires_in?為訪問令牌的過期時(shí)間,單位為秒。

  • 響應(yīng)字段?scope?為權(quán)限范圍

友情提示:/oauth/token?對(duì)應(yīng) TokenEndpoint 端點(diǎn),提供 OAuth2.0 的四種授權(quán)模式。感興趣的胖友,可以后續(xù)去擼擼。

②?POST?請(qǐng)求 http://localhost:8080/oauth/check_token 地址,校驗(yàn)訪問令牌的有效性。如下圖所示:

?

  • client-id?+?client-secret?進(jìn)行 Client 認(rèn)證 密碼模式的認(rèn)證
  • 密碼模式的認(rèn)證

請(qǐng)求和響應(yīng)比較簡(jiǎn)單,胖友自己瞅瞅即可。

2.2 搭建資源服務(wù)器

創(chuàng)建?lab-68-demo02-resource-server?項(xiàng)目,搭建資源服務(wù)器。

2.2.1 引入依賴

創(chuàng)建?pom.xml?文件,引入 Spring Security OAuth 依賴。

<?xml?version="1.0"?encoding="UTF-8"?> <project?xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/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><!--?實(shí)現(xiàn)對(duì)?Spring?MVC?的自動(dòng)配置?--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--?實(shí)現(xiàn)對(duì)?Spring?Security?OAuth2?的自動(dòng)配置?--><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:?9090security:oauth2:#?OAuth2?Client?配置,對(duì)應(yīng)?OAuth2ClientProperties?類client:client-id:?clientappclient-secret:?112233#?OAuth2?Resource?配置,對(duì)應(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?配置項(xiàng),OAuth2 Client 配置,對(duì)應(yīng) OAuth2ClientProperties 類。在這個(gè)配置項(xiàng)中,我們添加了客戶端的?client-id?和?client-secret。

為什么要添加這個(gè)配置項(xiàng)呢?因?yàn)橘Y源服務(wù)器會(huì)調(diào)用授權(quán)服務(wù)器的?/oauth/check_token?接口,而考慮到安全性,我們配置了該接口需要進(jìn)過客戶端認(rèn)證

友情提示:這里艿艿偷懶了,其實(shí)單獨(dú)給資源服務(wù)器配置一個(gè) Client 的?client-id?和?client-secret。我們可以把資源服務(wù)器理解成授權(quán)服務(wù)器的一個(gè)特殊的客戶端

②?security.oauth2.resource?配置項(xiàng),OAuth2 Resource 配置,對(duì)應(yīng) ResourceServerProperties 類。

這里,我們通過?token-info-uri?配置項(xiàng),設(shè)置使用授權(quán)服務(wù)器的?/oauth/check_token?接口,校驗(yàn)訪問令牌的有效性。

③?security.access-token-uri?配置項(xiàng),是我們自定義的,設(shè)置授權(quán)服務(wù)器的?oauth/token?接口,獲取訪問令牌。因?yàn)樯院笪覀儗⒃?LoginController 中,實(shí)現(xiàn)一個(gè)?/login?登錄接口。

2.2.3 OAuth2ResourceServerConfig

創(chuàng)建 OAuth2ResourceServerConfig 類,進(jìn)行資源服務(wù)器。代碼如下:

@Configuration @EnableResourceServer public?class?OAuth2ResourceServerConfig?extends?ResourceServerConfigurerAdapter?{@Overridepublic?void?configure(HttpSecurity?http)?throws?Exception?{http.authorizeRequests()//?設(shè)置?/login?無需權(quán)限訪問.antMatchers("/login").permitAll()//?設(shè)置其它請(qǐng)求,需要認(rèn)證后訪問.anyRequest().authenticated();}}

① 在類上添加?@EnableResourceServer?注解,聲明開啟 OAuth?資源服務(wù)器的功能。

同時(shí),繼承 ResourceServerConfigurerAdapter 類,進(jìn)行 OAuth?資源服務(wù)器的配置。

②?#configure(HttpSecurity http)?方法,設(shè)置 HTTP 權(quán)限。這里,我們?cè)O(shè)置?/login?接口無需權(quán)限訪問,其它接口認(rèn)證后可訪問。

這樣,客戶端在訪問資源服務(wù)器時(shí),其請(qǐng)求中的訪問令牌會(huì)被資源服務(wù)器調(diào)用授權(quán)服務(wù)器的?/oauth/check_token?接口,進(jìn)行校驗(yàn)訪問令牌的正確性。

2.2.4 ExampleController

創(chuàng)建 ExampleController 類,提供?/api/example/hello?接口,表示一個(gè)資源。代碼如下:

@RestController @RequestMapping("/api/example") public?class?ExampleController?{@RequestMapping("/hello")public?String?hello()?{return?"world";}}

2.2.5 ResourceServerApplication

創(chuàng)建 ResourceServerApplication 類,資源服務(wù)器的啟動(dòng)類。代碼如下:

@SpringBootApplication public?class?ResourceServerApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(ResourceServerApplication.class,?args);}}

2.2.6 簡(jiǎn)單測(cè)試(第一彈)

執(zhí)行 ResourceServerApplication 啟動(dòng)資源服務(wù)器。下面,我們來請(qǐng)求服務(wù)器的 <127.0.0.1:9090/api/example/hello> 接口,進(jìn)行相應(yīng)的測(cè)試。

① 首先,請(qǐng)求 <127.0.0.1:9090/api/example/hello> 接口,不帶訪問令牌,則請(qǐng)求會(huì)被攔截。如下圖所示:

不帶訪問令牌

② 然后,請(qǐng)求 <127.0.0.1:9090/api/example/hello> 接口,帶上錯(cuò)誤的訪問令牌,則請(qǐng)求會(huì)被攔截。如下圖所示:

錯(cuò)誤的訪問令牌

?

友情提示:訪問令牌需要在請(qǐng)求頭?"Authorization"?上設(shè)置,并且以?"Bearer "?開頭。

③ 最后,請(qǐng)求 <127.0.0.1:9090/api/example/hello> 接口,帶上正確的訪問令牌,則請(qǐng)求會(huì)被通過。如下圖所示:

正確的訪問令牌

?

2.2.7 LoginController

創(chuàng)建 LoginController 類,提供?/login?登錄接口。代碼如下:

@RestController @RequestMapping("/") public?class?LoginController?{@Autowiredprivate?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?對(duì)象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?對(duì)象OAuth2RestTemplate?restTemplate?=?new?OAuth2RestTemplate(resourceDetails);restTemplate.setAccessTokenProvider(new?ResourceOwnerPasswordAccessTokenProvider());//?<3>?獲取訪問令牌return?restTemplate.getAccessToken();}}

在?/login?接口中,資源服務(wù)器扮演的是一個(gè) OAuth?客戶端的角色,調(diào)用授權(quán)服務(wù)器的?/oauth/token?接口,使用密碼模式進(jìn)行授權(quán),獲得訪問令牌

①?<1>?處,創(chuàng)建 ResourceOwnerPasswordResourceDetails 對(duì)象,填寫密碼模式授權(quán)需要的請(qǐng)求參數(shù)。

②?<2>?處,創(chuàng)建 OAuth2RestTemplate 對(duì)象,它是 Spring Security OAuth 封裝的工具類,用于請(qǐng)求授權(quán)服務(wù)器。

同時(shí),將 ResourceOwnerPasswordAccessTokenProvider 設(shè)置到其中,表示使用密碼模式授權(quán)。

友情提示:這一步非常重要,艿艿在這里卡了非常非常非常久,一度自閉要放棄。

③?<3>?處,調(diào)用 OAuth2RestTemplate 的?#getAccessToken()?方法,調(diào)用授權(quán)服務(wù)器的?/oauth/token?接口,進(jìn)行密碼模式的授權(quán)。

注意,OAuth2RestTemplate 是有狀態(tài)的工具類,所以需要每次都重新創(chuàng)建。

2.2.8 簡(jiǎn)單測(cè)試(第二彈)

重新執(zhí)行 ResourceServerApplication 啟動(dòng)資源服務(wù)器。下面,我們來進(jìn)行?/login?接口的測(cè)試。

① 首先,請(qǐng)求 http://127.0.0.1:9090/login 接口,使用用戶用戶名密碼進(jìn)行登錄,獲得訪問令牌。如下圖所示:

測(cè)試?/login?接口

響應(yīng)結(jié)果和授權(quán)服務(wù)器的?/oauth/token?接口是一致的,因?yàn)榫褪钦{(diào)用它,嘿嘿~

② 然后,請(qǐng)求 <127.0.0.1:9090/api/example/hello> 接口,帶剛剛的訪問令牌,則請(qǐng)求會(huì)被通過。如下圖所示:

正確的訪問令牌

?

3. 授權(quán)碼模式

示例代碼對(duì)應(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)模式。它的特點(diǎn)就是通過客戶端的后臺(tái)服務(wù)器,與授權(quán)務(wù)器進(jìn)行互動(dòng)。

旁白君:一般情況下,在有客戶端的情況下,我們與第三方平臺(tái)常常采用這種方式。

授權(quán)碼模式

?

  • (A)用戶訪問客戶端,后者將前者跳轉(zhuǎn)到到授權(quán)服務(wù)器。

  • (B)用戶選擇是否給予客戶端授權(quán)。

  • (C)假設(shè)用戶給予授權(quán),授權(quán)服務(wù)器將跳轉(zhuǎn)到客戶端事先指定的"重定向 URI"(Redirection URI),同時(shí)附上一個(gè)授權(quán)碼

  • (D)客戶端收到授權(quán)碼,附上早先的"重定向 URI",向認(rèn)證服務(wù)器申請(qǐng)令牌。這一步是在客戶端的后臺(tái)的服務(wù)器上完成的,對(duì)用戶不可見。

  • (E)認(rèn)證服務(wù)器核對(duì)了授權(quán)碼重定向 URI,確認(rèn)無誤后,向客戶端發(fā)送訪問令牌

下面,我們來新建兩個(gè)項(xiàng)目,搭建一個(gè)授權(quán)碼模式的使用示例。如下圖所示:

項(xiàng)目結(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?項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動(dòng)點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用?"authorization_code"?授權(quán)碼模式,并設(shè)置回調(diào)地址。

🙂 注意,這里設(shè)置的回調(diào)地址,稍后我們會(huì)在「3.2 搭建資源服務(wù)器」中實(shí)現(xiàn)。

3.1.1 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(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)。請(qǐng)求參數(shù)說明如下:

  • client_id?參數(shù),必傳,為我們?cè)?OAuth2AuthorizationServer 中配置的 Client 的編號(hào)。

  • redirect_uri?參數(shù),可選,回調(diào)地址。當(dāng)然,如果?client_id?對(duì)應(yīng)的 Client 未配置?redirectUris?屬性,會(huì)報(bào)錯(cuò)。

  • response_type?參數(shù),必傳,返回結(jié)果為?code?授權(quán)碼

  • scope?參數(shù),可選,申請(qǐng)授權(quán)的 Scope 。如果多個(gè),使用逗號(hào)分隔。

  • state?參數(shù),可選,表示客戶端的當(dāng)前狀態(tài),可以指定任意值,授權(quán)服務(wù)器會(huì)原封不動(dòng)地返回這個(gè)值。

友情提示:state?參數(shù),未在上述 URL 中體現(xiàn)出來。

因?yàn)槲覀儾⑽?strong>登錄授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面。如下圖所示:

登錄界面

② 輸入用戶的賬號(hào)密碼「yunai/1024」進(jìn)行登錄。登錄完成后,進(jìn)入授權(quán)界面。如下圖所示:

旁白君:和我們?nèi)粘J褂玫尿v訊 QQ、微信、微博等等三方登錄,是一模一樣的,除了丑了點(diǎn),嘿嘿~

授權(quán)界面

③ 選擇?scope.read_userinfo?為 Approve 允許,點(diǎn)擊「Authorize」按鈕,完成授權(quán)操作。瀏覽器自動(dòng)重定向到 Redirection URI 地址,并且在 URI 上可以看到?code?授權(quán)碼。如下圖所示:

回調(diào)界面

?

友情提示:/oauth/authorize?對(duì)應(yīng) AuthorizationEndpoint 端點(diǎn)。

④ 因?yàn)槲覀儠簳r(shí)沒有啟動(dòng)資源服務(wù)器,所以顯示無法訪問。這里,我們先使用 Postman 模擬請(qǐng)求 http://localhost:8080/oauth/token 地址,使用授權(quán)碼獲取到訪問令牌。如下圖所示:

?

  • client-id?+?client-secret?進(jìn)行 Client 認(rèn)證 授權(quán)碼模式的認(rèn)證
  • 授權(quán)碼模式的認(rèn)證

    ?

請(qǐng)求說明:

  • 通過 Basic Auth 的方式,填寫?client-id?+?client-secret?作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。

  • 請(qǐng)求參數(shù)?grant_type?為?"authorization_code",表示使用授權(quán)碼模式

  • 請(qǐng)求參數(shù)?code,從授權(quán)服務(wù)器獲取到的授權(quán)碼

  • 請(qǐng)求參數(shù)?redirect_uri,Client 客戶端的?Redirection URI?地址。

注意,授權(quán)碼僅能使用一次,重復(fù)請(qǐng)求會(huì)報(bào)?Invalid authorization code:?錯(cuò)誤。如下圖所示:

授權(quán)碼模式的認(rèn)證 - 失敗

3.2 搭建資源服務(wù)器

復(fù)用?lab-68-demo02-resource-server?項(xiàng)目,主要是提供回調(diào)地址。如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 新建 CallbackController 類,提供?/callback?回調(diào)地址。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置?/callback?回調(diào)地址無需權(quán)限驗(yàn)證,不然回調(diào)都跳轉(zhuǎn)不過來哈。

3.2.1 CallbackController

創(chuàng)建 CallbackController 類,提供?/callback?回調(diào)地址,在獲取到授權(quán)碼時(shí),請(qǐng)求授權(quán)服務(wù)器,通過授權(quán)碼獲取訪問令牌。代碼如下:

@RestController @RequestMapping("/") public?class?CallbackController?{@Autowiredprivate?OAuth2ClientProperties?oauth2ClientProperties;@Value("${security.oauth2.access-token-uri}")private?String?accessTokenUri;@GetMapping("/callback")public?OAuth2AccessToken?login(@RequestParam("code")?String?code)?{//?創(chuàng)建?AuthorizationCodeResourceDetails?對(duì)象AuthorizationCodeResourceDetails?resourceDetails?=?new?AuthorizationCodeResourceDetails();resourceDetails.setAccessTokenUri(accessTokenUri);resourceDetails.setClientId(oauth2ClientProperties.getClientId());resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());//?創(chuàng)建?OAuth2RestTemplate?對(duì)象OAuth2RestTemplate?restTemplate?=?new?OAuth2RestTemplate(resourceDetails);restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code);?//?<1>?設(shè)置?coderestTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState("http://127.0.0.1:9090/callback");?//?<2>?通過這個(gè)方式,設(shè)置?redirect_uri?參數(shù)restTemplate.setAccessTokenProvider(new?AuthorizationCodeAccessTokenProvider());//?獲取訪問令牌return?restTemplate.getAccessToken();}}

代碼比較簡(jiǎn)單,還是使用 OAuth2RestTemplate 進(jìn)行請(qǐng)求授權(quán)服務(wù)器,胖友自己瞅瞅哈。

需要注意的是?<1>?和?<2>?處,設(shè)置請(qǐng)求授權(quán)服務(wù)器需要的?code?和?redirect_uri?參數(shù)。

3.2.2 簡(jiǎn)單測(cè)試

執(zhí)行 ResourceServerApplication 啟動(dòng)資源服務(wù)器。

重復(fù)「3.2.1 簡(jiǎn)單測(cè)試」的過程,成功獲取到訪問令牌。如下圖所示:

授權(quán)碼模式的認(rèn)證 - 成功

4. 簡(jiǎn)化模式

示例代碼對(duì)應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-implicit

  • 資源服務(wù)器:lab-68-demo02-resource-server

本小節(jié),我們來學(xué)習(xí)簡(jiǎn)化模式(Implicit)

簡(jiǎn)化模式,不通過第三方應(yīng)用程序的服務(wù)器,直接在瀏覽器中向授權(quán)服務(wù)器申請(qǐng)令牌,跳過了“授權(quán)碼”這個(gè)步驟,因此得名。所有步驟在瀏覽器中完成,令牌對(duì)訪問者是可見的,且客戶端不需要授權(quán)。

簡(jiǎn)化模式

?

  • (A)用戶訪問客戶端,后者將前者跳轉(zhuǎn)到到授權(quán)服務(wù)器。

  • (B)用戶選擇是否給予客戶端授權(quán)。

  • (C)假設(shè)用戶給予授權(quán),授權(quán)服務(wù)器將用戶導(dǎo)向客戶端指定的"重定向URI",并在 URI 的?Hash 部分包含了訪問令牌

  • (D)瀏覽器向資源服務(wù)器發(fā)出請(qǐng)求,其中不包括上一步收到的 Hash 值。

  • (E)資源服務(wù)器返回一個(gè)網(wǎng)頁,其中包含的代碼可以獲取 Hash 值中的令牌。

  • (F)瀏覽器執(zhí)行上一步獲得的腳本,提取出令牌。

  • (G)瀏覽器將令牌發(fā)給客戶端。

項(xiàng)目結(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?項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動(dòng)點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用?"implicit"?簡(jiǎn)化模式,并設(shè)置回調(diào)地址。

🙂 注意,這里設(shè)置的回調(diào)地址,稍后我們會(huì)在「4.2 搭建資源服務(wù)器」中實(shí)現(xiàn)。

4.2 搭建資源服務(wù)器

復(fù)用?lab-68-demo02-resource-server?項(xiàng)目,主要是提供回調(diào)地址。如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 新建 Callback02Controller 類,提供?/callback02?回調(diào)地址。代碼如下:

@RestController @RequestMapping("/") public?class?Callback02Controller?{@GetMapping("/callback02")public?String?login()?{return?"假裝這里有一個(gè)頁面";}} “

友情提示:考慮到暫時(shí)不想做頁面,所以這里先假裝一下,嘿嘿。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置?/callback02?回調(diào)地址無需權(quán)限驗(yàn)證,不然回調(diào)都跳轉(zhuǎn)不過來哈。

4.3 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(quán)服務(wù)器。
執(zhí)行 ResourceServerApplication 啟動(dòng)資源服務(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)。請(qǐng)求參數(shù)說明如下:

  • client_id?參數(shù),必傳,為我們?cè)?OAuth2AuthorizationServer 中配置的 Client 的編號(hào)。

  • redirect_uri?參數(shù),可選,回調(diào)地址。當(dāng)然,如果?client_id?對(duì)應(yīng)的 Client 未配置?redirectUris?屬性,會(huì)報(bào)錯(cuò)。

  • response_type?參數(shù),必傳,返回結(jié)果為?token?訪問令牌

  • scope?參數(shù),可選,申請(qǐng)授權(quán)的 Scope 。如果多個(gè),使用逗號(hào)分隔。

  • state?參數(shù),可選,表示客戶端的當(dāng)前狀態(tài),可以指定任意值,授權(quán)服務(wù)器會(huì)原封不動(dòng)地返回這個(gè)值。

友情提示:state?參數(shù),未在上述 URL 中體現(xiàn)出來。

因?yàn)槲覀儾⑽?strong>登錄授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面。如下圖所示:

登錄界面

② 輸入用戶的賬號(hào)密碼「yunai/1024」進(jìn)行登錄。登錄完成后,進(jìn)入授權(quán)界面。如下圖所示:

旁白君:和我們?nèi)粘J褂玫尿v訊 QQ、微信、微博等等三方登錄,是一模一樣的,除了丑了點(diǎn),嘿嘿~

授權(quán)界面

③ 選擇?scope.read_userinfo?為 Approve 允許,點(diǎn)擊「Authorize」按鈕,完成授權(quán)操作。瀏覽器自動(dòng)重定向到 Redirection URI 地址,并且在 URI 上的?Hash 部分可以看到?access_token?訪問令牌。如下圖所示:

回調(diào)界面

后續(xù),可以通過編寫 Javascript 腳本的代碼,獲取 URI 上的?Hash 部分的訪問令牌。

5. 客戶端模式

示例代碼對(duì)應(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ù)器進(jìn)行認(rèn)證。

嚴(yán)格地說,客戶端模式并不屬于 OAuth 框架所要解決的問題。在這種模式中,用戶直接向客戶端注冊(cè),客戶端以自己的名義要求授權(quán)服務(wù)器提供服務(wù),其實(shí)不存在授權(quán)問題。

旁白君:我們對(duì)接微信公眾號(hào)時(shí),就采用的客戶端模式。我們的后端服務(wù)器就扮演“客戶端”的角色,與微信公眾號(hào)的后端服務(wù)器進(jìn)行交互。

客戶端模式

?

  • (A)客戶端向授權(quán)服務(wù)器進(jìn)行身份認(rèn)證,并要求一個(gè)訪問令牌

  • (B)授權(quán)服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

下面,我們來新建兩個(gè)項(xiàng)目,搭建一個(gè)客戶端模式的使用示例。如下圖所示:

項(xiàng)目結(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?項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動(dòng)點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 刪除 SecurityConfig 配置類,因?yàn)榭蛻舳四J较?#xff0c;無需 Spring Security 提供用戶的認(rèn)證功能。

但是,Spring Security OAuth 需要一個(gè) PasswordEncoder Bean,否則會(huì)報(bào)錯(cuò),因此我們?cè)?OAuth2AuthorizationServerConfig 類的?#passwordEncoder()?方法進(jìn)行創(chuàng)建。

② 修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用?"client_credentials"?客戶端模式。

5.1.1 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(quán)服務(wù)器。下面,我們使用?Postman 模擬一個(gè) Client

①?POST?請(qǐng)求 http://localhost:8080/oauth/token 地址,使用客戶端模式進(jìn)行授權(quán)。如下圖所示:

  • client-id?+?client-secret?進(jìn)行 Client 認(rèn)證 客戶端模式的認(rèn)證

    ?

  • 客戶端模式的認(rèn)證

    ?

請(qǐng)求說明:

  • 通過 Basic Auth 的方式,填寫?client-id?+?client-secret?作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。

  • 請(qǐng)求參數(shù)?grant_type?為?"client_credentials",表示使用客戶端模式

響應(yīng)就是訪問令牌,胖友自己瞅瞅即可。

5.2 搭建資源服務(wù)器

復(fù)用?lab-68-demo02-resource-server?項(xiàng)目,修改點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 新建 ClientLoginController 類,提供?/client-login?接口,實(shí)現(xiàn)調(diào)用授權(quán)服務(wù)器,進(jìn)行客戶端模式的授權(quán),獲得訪問令牌。代碼如下:

@RestController @RequestMapping("/") public?class?ClientLoginController?{@Autowiredprivate?OAuth2ClientProperties?oauth2ClientProperties;@Value("${security.oauth2.access-token-uri}")private?String?accessTokenUri;@PostMapping("/client-login")public?OAuth2AccessToken?login()?{//?創(chuàng)建?ClientCredentialsResourceDetails?對(duì)象ClientCredentialsResourceDetails?resourceDetails?=?new?ClientCredentialsResourceDetails();resourceDetails.setAccessTokenUri(accessTokenUri);resourceDetails.setClientId(oauth2ClientProperties.getClientId());resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());//?創(chuàng)建?OAuth2RestTemplate?對(duì)象OAuth2RestTemplate?restTemplate?=?new?OAuth2RestTemplate(resourceDetails);restTemplate.setAccessTokenProvider(new?ClientCredentialsAccessTokenProvider());//?獲取訪問令牌return?restTemplate.getAccessToken();}}

代碼比較簡(jiǎn)單,還是使用 OAuth2RestTemplate 進(jìn)行請(qǐng)求授權(quán)服務(wù)器,胖友自己瞅瞅哈。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置?/client-login?接口無需權(quán)限驗(yàn)證,不然無法調(diào)用哈。

5.2.1 簡(jiǎn)單測(cè)試

執(zhí)行 ResourceServerApplication 啟動(dòng)資源服務(wù)器。

① 使用「5.1.1 簡(jiǎn)單測(cè)試」小節(jié)獲得的訪問令牌,請(qǐng)求 <127.0.0.1:9090/api/example/hello> 接口時(shí)帶上,則請(qǐng)求會(huì)被通過。如下圖所示:

正確的訪問令牌

② 請(qǐng)求 http://127.0.0.1:9090/clientlogin 接口,使用客戶端模式進(jìn)行授權(quán),獲得訪問令牌。如下圖所示:

測(cè)試?client-login?接口

響應(yīng)結(jié)果和授權(quán)服務(wù)器的?/oauth/token?接口是一致的,因?yàn)榫褪钦{(diào)用它,嘿嘿~

6. 合并服務(wù)器

旁白君:這個(gè)小節(jié)的標(biāo)題,艿艿有點(diǎn)不知道怎么取了,就先叫合并服務(wù)器吧 = =!

在項(xiàng)目比較小時(shí),考慮到節(jié)省服務(wù)器資源,會(huì)考慮將授權(quán)服務(wù)器和資源服務(wù)器合并到一個(gè)項(xiàng)目中,避免啟動(dòng)多個(gè) Java 進(jìn)程。良心的艿艿,編寫了四種授權(quán)模式的示例,如下圖所示:

示例項(xiàng)目

  • 基于密碼模式的示例:lab-68-demo01-resource-owner-password-credentials-server

  • 基于授權(quán)碼模式的示例:lab-68-demo01-authorization-code-server

  • 基于簡(jiǎn)化模式的示例:lab-68-demo01-implicit-server

  • 基于客戶端模式的示例:lab-68-demo01-client-credentials-server

具體的代碼實(shí)現(xiàn),實(shí)際和上述每個(gè)授權(quán)模式對(duì)應(yīng)的小節(jié)是基本一致的,只是說將代碼“”在了一個(gè)項(xiàng)目中。嘿嘿~

7. 刷新令牌

示例代碼對(duì)應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials

在 OAuth2.0 中,一共有兩類令牌:

  • 訪問令牌(Access Token)

  • 刷新令牌(Refresh Token)

訪問令牌過期時(shí),我們可以使用刷新令牌向授權(quán)服務(wù)器獲取一個(gè)的訪問令牌。

可能會(huì)胖友有疑惑,為什么會(huì)有刷新令牌呢?每次請(qǐng)求資源服務(wù)器時(shí),都會(huì)在請(qǐng)求上帶上訪問令牌,這樣它的泄露風(fēng)險(xiǎn)是相對(duì)高的。

因此,出于安全性的考慮,訪問令牌的過期時(shí)間比較短,刷新令牌的過期時(shí)間比較長(zhǎng)。這樣,如果訪問令牌即使被盜用走,那么在一定的時(shí)間后,訪問令牌也能在較短的時(shí)間吼過期。當(dāng)然,安全也是相對(duì)的,如果使用刷新令牌后,獲取到新的訪問令牌,訪問令牌后續(xù)可能被盜用。

艿艿整理了下,大家常用開放平臺(tái)的令牌過期時(shí)間,讓大家更好的理解:

開放平臺(tái)Access Token 有效期Refresh Token 有效期
微信開放平臺(tái)2 小時(shí)未知
騰訊開放平臺(tái)90 天未知
小米開放平臺(tái)90 天10 年

7.1 示例項(xiàng)目

下面,復(fù)制出?lab-68-demo03-authorization-server-with-client-credentials?項(xiàng)目,搭建提供訪問令牌授權(quán)服務(wù)器。改動(dòng)點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 在 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。

友情提示:如果不進(jìn)行 UserDetailsService 的設(shè)置,在使用刷新令牌獲取新的訪問令牌時(shí),會(huì)拋出異常。

7.2 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(quán)服務(wù)器。下面,我們使用?Postman 模擬一個(gè) Client

①?POST?請(qǐng)求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

密碼模式的認(rèn)證

額外多返回了?refresh_token?刷新令牌。

②?POST?請(qǐng)求 http://localhost:8080/oauth/token 地址,使用刷新令牌模式進(jìn)行授權(quán)。如下圖所示:

刷新令牌模式的認(rèn)證

請(qǐng)求說明:

  • 通過 Basic Auth 的方式,填寫?client-id?+?client-secret?作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。

  • 請(qǐng)求參數(shù)?grant_type?為?"refresh_token",表示使用刷新令牌模式

  • 請(qǐng)求參數(shù)?refresh_token,表示刷新令牌

在響應(yīng)中,返回了新的?access_token?訪問令牌。注意,老的?access_token?訪問令牌會(huì)失效,無法繼續(xù)使用。

8. 刪除令牌

示例代碼對(duì)應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials

在用戶登出系統(tǒng)時(shí),我們會(huì)有刪除令牌的需求。雖然說,可以通過客戶端本地刪除令牌的方式實(shí)現(xiàn)。但是,考慮到真正的徹底的實(shí)現(xiàn)刪除令牌,必然服務(wù)端自身需要?jiǎng)h除令牌。

友情提示:客戶端本地刪除令牌的方式實(shí)現(xiàn),指的是清楚本地 Cookie、localStorage 的令牌緩存。

在 Spring Security OAuth2 中,并沒有提供內(nèi)置的接口,所以需要自己去實(shí)現(xiàn)。筆者參看 《Spring Security OAuth2 – Simple Token Revocation》 文檔,實(shí)現(xiàn)刪除令牌的 API 接口。

具體的實(shí)現(xiàn),通過調(diào)用 ConsumerTokenServices 的?#revokeToken(String tokenValue)?方法,刪除訪問令牌和刷新令牌。如下圖所示:

ConsumerTokenServices 實(shí)現(xiàn)類

8.1 示例項(xiàng)目

下面,我們直接在授權(quán)服務(wù)器?lab-68-demo03-authorization-server-with-resource-owner-password-credentials?項(xiàng)目,修改接入刪除令牌的功能。改動(dòng)點(diǎn)如下圖所示:

項(xiàng)目改動(dòng)點(diǎn)

① 創(chuàng)建 TokenDemoController 類,提供?/token/demo/revoke?接口,調(diào)用 ConsumerTokenServices 的?#revokeToken(String tokenValue)?方法,刪除訪問令牌和刷新令牌。代碼如下:

@RestController @RequestMapping("/token/demo") public?class?TokenDemoController?{@Autowiredprivate?ConsumerTokenServices?tokenServices;@PostMapping(value?=?"/revoke")public?boolean?revokeToken(@RequestParam("token")?String?token)?{return?tokenServices.revokeToken(token);}}

② 在 SecurityConfig 配置類,設(shè)置?/token/demo/revoke?接口無需授權(quán),方便測(cè)試。代碼如下:

//?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 簡(jiǎn)單測(cè)試

執(zhí)行 AuthorizationServerApplication 啟動(dòng)授權(quán)服務(wù)器。下面,我們使用?Postman 模擬一個(gè) Client

①?POST?請(qǐng)求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

密碼模式的認(rèn)證

②?POST?請(qǐng)求 http://localhost:8080/token/demo/revoke 地址,刪除令牌。如下圖所示:

刪除令牌

刪除成功。后續(xù),胖友可以自己調(diào)用授權(quán)服務(wù)器的?oauth/check_token?接口,測(cè)試訪問令牌是否已經(jīng)被刪除。

666. 彩蛋

至此,我們完整學(xué)習(xí) Spring Security OAuth 框架。不過 Spring 團(tuán)隊(duì)宣布該框架處于 Deprecation?廢棄狀態(tài)。如下圖所示:

Spring Security OAuth 被廢棄

同時(shí),Spring 團(tuán)隊(duì)正在實(shí)現(xiàn)新的 Spring Authorization Server?授權(quán)服務(wù)器,目前還處于 Experimental?實(shí)驗(yàn)狀態(tài)。

實(shí)際項(xiàng)目中,根據(jù)艿艿了解到的情況,很少項(xiàng)目會(huì)直接采用 Spring Security OAuth 框架,而是自己參考它進(jìn)行 OAuth2.0 的實(shí)現(xiàn)。并且,一般只會(huì)實(shí)現(xiàn)密碼授權(quán)模式。


在本文中,我們采用基于內(nèi)存的 InMemoryTokenStore,實(shí)現(xiàn)訪問令牌和刷新令牌的存儲(chǔ)。它會(huì)存在兩個(gè)明顯的缺點(diǎn)

  • 重啟授權(quán)服務(wù)器時(shí),令牌信息會(huì)丟失,導(dǎo)致用戶需要重新授權(quán)。

  • 多個(gè)授權(quán)服務(wù)器時(shí),令牌信息無法共享,導(dǎo)致用戶一會(huì)授權(quán)成功,一會(huì)授權(quán)失敗。

因此,下一篇《芋道 Spring Security OAuth2 存儲(chǔ)器》文章,我們來學(xué)習(xí) Spring Security OAuth 提供的基于數(shù)據(jù)庫和?Redis的存儲(chǔ)器。走起~

總結(jié)

以上是生活随笔為你收集整理的可能是第二好的 Spring OAuth 2.0 文章,艿艿端午在家写了 3 天~的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。