Spring Security OAuth2分布式系统认证解决方案
目錄
- 1 什么是分布式系統(tǒng)
- 2 分布式認(rèn)證需求
- 3 分布式認(rèn)證方案
- 3.1 選型分析
- 3.2 技術(shù)方案
- 4 OAuth2.0
- 4.1 OAuth2.0介紹
- 4.2 Spring Cloud Security OAuth2
- 4.2.1 環(huán)境介紹
- 4.2.2 環(huán)境搭建
- 4.2.2.1 父工程
- 4.2.2.2 創(chuàng)建UAA授權(quán)服務(wù)工程
- 4.2.2.3 創(chuàng)建Order資源服務(wù)工程
- 4.2.2.授權(quán)服務(wù)器配置
- 4.2.2.1 EnableAuthorizationServer
- 4.2.2.1.配置客戶端詳細(xì)信息
- 4.2.2.2.管理令牌
- 4.2.2.3.令牌訪問端點(diǎn)配置
- 4.2.2.4.令牌端點(diǎn)的安全約束
- 4.2.2.5 web安全配置
- 4.2.3.授權(quán)碼模式
- 4.2.3.1 授權(quán)碼模式介紹
- 4.2.3.2 測試
- 4.2.4. 簡化模式
- 4.2.4.1 簡化模式介紹
- 4.2.5.密碼模式
- 4.2.5.1 授權(quán)碼模式介紹
- 4.2.5.2 測試
- 4.2.6.客戶端模式
- 4.2.6.1 客戶端模式介紹
- 4.2.7.資源服務(wù)測試
- 4.2.7.1 資源服務(wù)器配置
- 4.2.7.2 驗(yàn)證token
- 4.2.7.3 編寫資源
- 4.2.7.4 添加安全訪問控制
- 4.2.7.5 測試
1 什么是分布式系統(tǒng)
隨著軟件環(huán)境和需求的變化 ,軟件的架構(gòu)由單體結(jié)構(gòu)演變?yōu)榉植际郊軜?gòu),具有分布式架構(gòu)的系統(tǒng)叫分布式系統(tǒng),分布式系統(tǒng)的運(yùn)行通常依賴網(wǎng)絡(luò),它將單體結(jié)構(gòu)的系統(tǒng)分為若干服務(wù),服務(wù)之間通過網(wǎng)絡(luò)交互來完成用戶的業(yè)務(wù)處理,當(dāng)前流行的微服務(wù)架構(gòu)就是分布式系統(tǒng)架構(gòu),如下圖:
分布式系統(tǒng)具體如下基本特點(diǎn):
1、分布性:每個(gè)部分都可以獨(dú)立部署,服務(wù)之間交互通過網(wǎng)絡(luò)進(jìn)行通信,比如:訂單服務(wù)、商品服務(wù)。
2、伸縮性:每個(gè)部分都可以集群方式部署,并可針對部分結(jié)點(diǎn)進(jìn)行硬件及軟件擴(kuò)容,具有一定的伸縮能力。
3、共享性:每個(gè)部分都可以作為共享資源對外提供服務(wù),多個(gè)部分可能有操作共享資源的情況。
4、開放性:每個(gè)部分根據(jù)需求都可以對外發(fā)布共享資源的訪問接口,并可允許第三方系統(tǒng)訪問。
2 分布式認(rèn)證需求
分布式系統(tǒng)的每個(gè)服務(wù)都會(huì)有認(rèn)證、授權(quán)的需求,如果每個(gè)服務(wù)都實(shí)現(xiàn)一套認(rèn)證授權(quán)邏輯會(huì)非常冗余,考慮分布式系統(tǒng)共享性的特點(diǎn),需要由獨(dú)立的認(rèn)證服務(wù)處理系統(tǒng)認(rèn)證授權(quán)的請求;考慮分布式系統(tǒng)開放性的特點(diǎn),不僅對系統(tǒng)內(nèi)部服務(wù)提供認(rèn)證,對第三方系統(tǒng)也要提供認(rèn)證。分布式認(rèn)證的需求總結(jié)如下:
統(tǒng)一認(rèn)證授權(quán)
提供獨(dú)立的認(rèn)證服務(wù),統(tǒng)一處理認(rèn)證授權(quán)。
無論是不同類型的用戶,還是不同種類的客戶端(web端,H5、APP),均采用一致的認(rèn)證、權(quán)限、會(huì)話機(jī)制,實(shí)現(xiàn)統(tǒng)一認(rèn)證授權(quán)。
實(shí)現(xiàn)統(tǒng)一則認(rèn)證方式必須可擴(kuò)展,支持各種認(rèn)證需求,比如:用戶名密碼認(rèn)證、短信驗(yàn)證碼、二維碼、人臉識別等認(rèn)證方式,并可以非常靈活的切換。
應(yīng)用接入認(rèn)證
應(yīng)提供擴(kuò)展和開放能力,提供安全的系統(tǒng)對接機(jī)制,并可開放部分API給接入第三方使用,一方應(yīng)用(內(nèi)部 系統(tǒng)服務(wù))和三方應(yīng)用(第三方應(yīng)用)均采用統(tǒng)一機(jī)制接入。
3 分布式認(rèn)證方案
3.1 選型分析
1、基于session的認(rèn)證方式
在分布式的環(huán)境下,基于session的認(rèn)證會(huì)出現(xiàn)一個(gè)問題,每個(gè)應(yīng)用服務(wù)都需要在session中存儲(chǔ)用戶身份信息,通過負(fù)載均衡將本地的請求分配到另一個(gè)應(yīng)用服務(wù)需要將session信息帶過去,否則會(huì)重新認(rèn)證。
這個(gè)時(shí)候,通常的做法有下面幾種:
Session復(fù)制:多臺(tái)應(yīng)用服務(wù)器之間同步session,使session保持一致,對外透明。
Session黏貼:當(dāng)用戶訪問集群中某臺(tái)服務(wù)器后,強(qiáng)制指定后續(xù)所有請求均落到此機(jī)器上。
Session集中存儲(chǔ):將Session存入分布式緩存中,所有服務(wù)器應(yīng)用實(shí)例統(tǒng)一從分布式緩存中存取Session。
總體來講,基于session認(rèn)證的認(rèn)證方式,可以更好的在服務(wù)端對會(huì)話進(jìn)行控制,且安全性較高。但是,session機(jī)制方式基于cookie,在復(fù)雜多樣的移動(dòng)客戶端上不能有效的使用,并且無法跨域,另外隨著系統(tǒng)的擴(kuò)展需提高session的復(fù)制、黏貼及存儲(chǔ)的容錯(cuò)性。
2、基于token的認(rèn)證方式
基于token的認(rèn)證方式,服務(wù)端不用存儲(chǔ)認(rèn)證數(shù)據(jù),易維護(hù)擴(kuò)展性強(qiáng), 客戶端可以把token 存在任意地方,并且可以實(shí)現(xiàn)web和app統(tǒng)一認(rèn)證機(jī)制。其缺點(diǎn)也很明顯,token由于自包含信息,因此一般數(shù)據(jù)量較大,而且每次請求都需要傳遞,因此比較占帶寬。另外,token的簽名驗(yàn)簽操作也會(huì)給cpu帶來額外的處理負(fù)擔(dān)。
3.2 技術(shù)方案
根據(jù) 選型的分析,決定采用基于token的認(rèn)證方式,它的優(yōu)點(diǎn)是:
1、適合統(tǒng)一認(rèn)證的機(jī)制,客戶端、一方應(yīng)用、三方應(yīng)用都遵循一致的認(rèn)證機(jī)制。
2、token認(rèn)證方式對第三方應(yīng)用接入更適合,因?yàn)樗_放,可使用當(dāng)前有流行的開放協(xié)議Oauth2.0、JWT等。
3、一般情況服務(wù)端無需存儲(chǔ)會(huì)話信息,減輕了服務(wù)端的壓力。
分布式系統(tǒng)認(rèn)證技術(shù)方案見下圖:
流程描述:
(1)用戶通過接入方(應(yīng)用)登錄,接入方采取OAuth2.0方式在統(tǒng)一認(rèn)證服務(wù)(UAA)中認(rèn)證。
(2)認(rèn)證服務(wù)(UAA)調(diào)用驗(yàn)證該用戶的身份是否合法,并獲取用戶權(quán)限信息。
(3)認(rèn)證服務(wù)(UAA)獲取接入方權(quán)限信息,并驗(yàn)證接入方是否合法。
(4)若登錄用戶以及接入方都合法,認(rèn)證服務(wù)生成jwt令牌返回給接入方,其中jwt中包含了用戶權(quán)限及接入方權(quán)限。
(5)后續(xù),接入方攜帶jwt令牌對API網(wǎng)關(guān)內(nèi)的微服務(wù)資源進(jìn)行訪問。
(6)API網(wǎng)關(guān)對令牌解析、并驗(yàn)證接入方的權(quán)限是否能夠訪問本次請求的微服務(wù)。
(7)如果接入方的權(quán)限沒問題,API網(wǎng)關(guān)將原請求header中附加解析后的明文Token,并將請求轉(zhuǎn)發(fā)至微服務(wù)。
(8)微服務(wù)收到請求,明文token中包含登錄用戶的身份和權(quán)限信息。因此后續(xù)微服務(wù)自己可以干兩件事:1,用戶授權(quán)攔截(看當(dāng)前用戶是否有權(quán)訪問該資源)2,將用戶信息存儲(chǔ)進(jìn)當(dāng)前線程上下文(有利于后續(xù)業(yè)務(wù)邏輯隨時(shí)獲取當(dāng)前用戶信息)
流程所涉及到UAA服務(wù)、API網(wǎng)關(guān)這三個(gè)組件職責(zé)如下:
1)統(tǒng)一認(rèn)證服務(wù)(UAA)
它承載了OAuth2.0接入方認(rèn)證、登入用戶的認(rèn)證、授權(quán)以及生成令牌的職責(zé),完成實(shí)際的用戶認(rèn)證、授權(quán)功能。
2)API網(wǎng)關(guān)
作為系統(tǒng)的唯一入口,API網(wǎng)關(guān)為接入方提供定制的API集合,它可能還具有其它職責(zé),如身份驗(yàn)證、監(jiān)控、負(fù)載均衡、緩存等。API網(wǎng)關(guān)方式的核心要點(diǎn)是,所有的接入方和消費(fèi)端都通過統(tǒng)一的網(wǎng)關(guān)接入微服務(wù),在網(wǎng)關(guān)層處理所有的非業(yè)務(wù)功能。
4 OAuth2.0
4.1 OAuth2.0介紹
OAuth(開放授權(quán))是一個(gè)開放標(biāo)準(zhǔn),允許用戶授權(quán)第三方應(yīng)用訪問他們存儲(chǔ)在另外的服務(wù)提供者上的信息,而不需要將用戶名和密碼提供給第三方應(yīng)用或分享他們數(shù)據(jù)的所有內(nèi)容。OAuth2.0是OAuth協(xié)議的延續(xù)版本,但不向后兼容OAuth 1.0即完全廢止了OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH認(rèn)證服務(wù),這些都足以說明OAUTH標(biāo)準(zhǔn)逐漸成為開放資源授權(quán)的標(biāo)準(zhǔn)。
Oauth協(xié)議目前發(fā)展到2.0版本,1.0版本過于復(fù)雜,2.0版本已得到廣泛應(yīng)用。
參考:https://baike.baidu.com/item/oAuth/7153134?fr=aladdin
Oauth 協(xié)議:https://tools.ietf.org/html/rfc6749
下邊分析一個(gè) Oauth2認(rèn)證的例子,通過例子去理解OAuth2.0協(xié)議的認(rèn)證流程,本例子是某個(gè)客戶端網(wǎng)站使用微信認(rèn)證的過程,這個(gè)過程的簡要描述如下:
用戶借助微信認(rèn)證登錄客戶端網(wǎng)站,用戶就不用單獨(dú)在客戶端注冊用戶,怎么樣算認(rèn)證成功嗎?客戶端網(wǎng)站需要成功從微信獲取用戶的身份信息則認(rèn)為用戶認(rèn)證成功,那如何從微信獲取用戶的身份信息?用戶信息的擁有者是用戶本人,微信需要經(jīng)過用戶的同意方可為客戶端網(wǎng)站生成令牌,客戶端網(wǎng)站拿此令牌方可從微信獲取用戶的信息。
1、客戶端請求第三方授權(quán)
用戶進(jìn)入客戶端的登錄頁面,點(diǎn)擊微信的圖標(biāo)以微信賬號登錄系統(tǒng),用戶是自己在微信里信息的資源擁有者。
點(diǎn)擊“微信”出現(xiàn)一個(gè)二維碼,此時(shí)用戶掃描二維碼,開始給客戶端網(wǎng)站授權(quán)。
2、資源擁有者同意給客戶端授權(quán)
資源擁有者掃描二維碼表示資源擁有者同意給客戶端授權(quán),微信會(huì)對資源擁有者的身份進(jìn)行驗(yàn)證, 驗(yàn)證通過后,微信會(huì)詢問用戶是否給授權(quán)客戶端網(wǎng)站訪問自己的微信數(shù)據(jù),用戶點(diǎn)擊“確認(rèn)登錄”表示同意授權(quán),微信認(rèn)證服務(wù)器會(huì)頒發(fā)一個(gè)授權(quán)碼,并重定向到客戶端的網(wǎng)站。
3、客戶端獲取到授權(quán)碼,請求認(rèn)證服務(wù)器申請令牌:
此過程用戶看不到,客戶端應(yīng)用程序請求認(rèn)證服務(wù)器,請求攜帶授權(quán)碼。
4、認(rèn)證服務(wù)器向客戶端響應(yīng)令牌:
微信認(rèn)證服務(wù)器驗(yàn)證了客戶端請求的授權(quán)碼,如果合法則給客戶端頒發(fā)令牌,令牌是客戶端訪問資源的通行證。此交互過程用戶看不到,當(dāng)客戶端拿到令牌后,用戶在客戶端網(wǎng)站看到已經(jīng)登錄成功。
5、客戶端請求資源服務(wù)器的資源
客戶端攜帶令牌訪問資源服務(wù)器的資源。
客戶端網(wǎng)站攜帶令牌請求訪問微信服務(wù)器獲取用戶的基本信息。
6、資源服務(wù)器返回受保護(hù)資源
資源服務(wù)器校驗(yàn)令牌的合法性,如果合法則向用戶響應(yīng)資源信息內(nèi)容。
以上認(rèn)證授權(quán)詳細(xì)的執(zhí)行流程如下:
通過上邊的例子我們大概了解了OAauth2.0的認(rèn)證過程,下邊我們看OAuth2.0認(rèn)證流程:
引自O(shè)Aauth2.0協(xié)議rfc6749 https://tools.ietf.org/html/rfc6749
OAauth2.0包括以下角色:
1、客戶端
本身不存儲(chǔ)資源,需要通過資源擁有者的授權(quán)去請求資源服務(wù)器的資源,比如:Android客戶端、Web客戶端(瀏覽器端)、微信客戶端等。
2、資源擁有者
通常為用戶,也可以是應(yīng)用程序,即該資源的擁有者。
3、授權(quán)服務(wù)器(也稱認(rèn)證服務(wù)器)
用于服務(wù)提供商對資源擁有的身份進(jìn)行認(rèn)證、對訪問資源進(jìn)行授權(quán),認(rèn)證成功后會(huì)給客戶端發(fā)放令牌(access_token),作為客戶端訪問資源服務(wù)器的憑據(jù)。本例為微信的認(rèn)證服務(wù)器。
4、資源服務(wù)器
存儲(chǔ)資源的服務(wù)器,本例子為微信存儲(chǔ)的用戶信息。
現(xiàn)在還有一個(gè)問題,服務(wù)提供商能允許隨便一個(gè)客戶端就接入到它的授權(quán)服務(wù)器嗎?答案是否定的,服務(wù)提供商會(huì)給準(zhǔn)入的接入方一個(gè)身份,用于接入時(shí)的憑據(jù):
client_id:客戶端標(biāo)識 client_secret:客戶端秘鑰
因此,準(zhǔn)確來說,授權(quán)服務(wù)器對兩種OAuth2.0中的兩個(gè)角色進(jìn)行認(rèn)證授權(quán),分別是資源擁有者、客戶端。
4.2 Spring Cloud Security OAuth2
4.2.1 環(huán)境介紹
Spring-Security-OAuth2是對OAuth2的一種實(shí)現(xiàn),并且跟我們之前學(xué)習(xí)的Spring Security相輔相成,與Spring Cloud體系的集成也非常便利,接下來,我們需要對它進(jìn)行學(xué)習(xí),最終使用它來實(shí)現(xiàn)我們設(shè)計(jì)的分布式認(rèn)證授權(quán)解決方案。
OAuth2.0的服務(wù)提供方涵蓋兩個(gè)服務(wù),即授權(quán)服務(wù) (Authorization Server,也叫認(rèn)證服務(wù)) 和資源服務(wù) (Resource Server),使用 Spring Security OAuth2 的時(shí)候你可以選擇把它們在同一個(gè)應(yīng)用程序中實(shí)現(xiàn),也可以選擇建立使用同一個(gè)授權(quán)服務(wù)的多個(gè)資源服務(wù)。
**授權(quán)服務(wù) (Authorization Server)**應(yīng)包含對接入端以及登入用戶的合法性進(jìn)行驗(yàn)證并頒發(fā)token等功能,對令牌的請求端點(diǎn)由 Spring MVC 控制器進(jìn)行實(shí)現(xiàn),下面是配置一個(gè)認(rèn)證服務(wù)必須要實(shí)現(xiàn)的endpoints:
- AuthorizationEndpoint 服務(wù)于認(rèn)證請求。默認(rèn) URL: /oauth/authorize 。
- TokenEndpoint 服務(wù)于訪問令牌的請求。默認(rèn) URL: /oauth/token 。
資源服務(wù) (Resource Server),應(yīng)包含對資源的保護(hù)功能,對非法請求進(jìn)行攔截,對請求中token進(jìn)行解析鑒權(quán)等,下面的過濾器用于實(shí)現(xiàn) OAuth 2.0 資源服務(wù):
- OAuth2AuthenticationProcessingFilter 用來對請求給出的身份令牌解析鑒權(quán)。
本教程分別創(chuàng)建uaa授權(quán)服務(wù)(也可叫認(rèn)證服務(wù))和order訂單資源服務(wù)。
認(rèn)證流程如下:
1、客戶端請求UAA授權(quán)服務(wù)進(jìn)行認(rèn)證。
2、認(rèn)證通過后由UAA頒發(fā)令牌。
3、客戶端攜帶令牌Token請求資源服務(wù)。
4 、資源服務(wù)校驗(yàn)令牌的合法性,合法即返回資源信息。
4.2.2 環(huán)境搭建
4.2.2.1 父工程
創(chuàng)建maven工程作為父工程,依賴如下:
<?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"><modelVersion>4.0.0</modelVersion><groupId>com.lw.security</groupId><artifactId>distributed-security</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>javax.interceptor</groupId> <artifactId>javax.interceptor-api</artifactId><version>1.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId><version>1.0.10.RELEASE</version></dependency><dependency><groupId>org.springframework.security.oauth.boot</groupId><artifactId>spring-security-oauth2-autoconfigure</artifactId><version>2.1.3.RELEASE</version></dependency></dependencies></dependencyManagement><build><finalName>${project.name}</finalName><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><includes><include>**/*</include></includes></resource><resource> <directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource></resources><plugins><!--<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin>--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><artifactId>maven-resources-plugin</artifactId><configuration><encoding>utf-8</encoding><useDefaultDelimiters>true</useDefaultDelimiters></configuration></plugin></plugins></build> </project>4.2.2.2 創(chuàng)建UAA授權(quán)服務(wù)工程
1、創(chuàng)建distributed-security-uaa
創(chuàng)建distributed-security-uaa作為授權(quán)服務(wù)工程,依賴如下:
<?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>distributed-security</artifactId><groupId>com.lw.security</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>distributed-security-uaa</artifactId><dependencies><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>com.netflix.hystrix</groupId><artifactId>hystrix-javanica</artifactId></dependency><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-commons</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-jwt</artifactId></dependency><dependency><groupId>javax.interceptor</groupId><artifactId>javax.interceptor-api</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies> </project>工程結(jié)構(gòu)如下:
2、啟動(dòng)類
本工程采用SpringBoot開發(fā),每個(gè)工程編寫一個(gè)啟動(dòng)類:
@SpringBootApplication @EnableDiscoveryClient @EnableHystrix @EnableFeignClients(basePackages = {"com.lw.security.distributed.uaa"}) public class UAAServer {public static void main(String[] args) {SpringApplication.run(UAAServer.class, args);} }3、配置文件
在resources下創(chuàng)建application.properties
spring.application.name=uaa-service server.port=53020 spring.main.allow-bean-definition-overriding = true logging.level.root = debug logging.level.org.springframework.web = info spring.http.encoding.enabled = true spring.http.encoding.charset = UTF-8 spring.http.encoding.force = true server.tomcat.remote_ip_header = x-forwarded-for server.tomcat.protocol_header = x-forwarded-proto server.use-forward-headers = true server.servlet.context-path = /uaa spring.freemarker.enabled = true spring.freemarker.suffix = .html spring.freemarker.request-context-attribute = rc spring.freemarker.content-type = text/html spring.freemarker.charset = UTF-8 spring.mvc.throw-exception-if-no-handler-found = true spring.resources.add-mappings = false spring.datasource.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true spring.datasource.username = root spring.datasource.password = mysql spring.datasource.driver-class-name = com.mysql.jdbc.Driver #eureka.client.serviceUrl.defaultZone = http://localhost:53000/eureka/ #eureka.instance.preferIpAddress = true #eureka.instance.instance-id = ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}} management.endpoints.web.exposure.include = refresh,health,info,env feign.hystrix.enabled = true feign.compression.request.enabled = true feign.compression.request.mime-types[0] = text/xml feign.compression.request.mime-types[1] = application/xml feign.compression.request.mime-types[2] = application/json feign.compression.request.min-request-size = 2048 feign.compression.response.enabled = true4.2.2.3 創(chuàng)建Order資源服務(wù)工程
本工程為Order訂單服務(wù)工程,訪問本工程的資源需要認(rèn)證通過。
本工程的目的主要是測試認(rèn)證授權(quán)的功能,所以不涉及訂單管理相關(guān)業(yè)務(wù)。
1、創(chuàng)建Order工程
<?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>distributed-security</artifactId><groupId>com.lw.security</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>distributed-security-order</artifactId> <dependencies><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>javax.interceptor</groupId><artifactId>javax.interceptor-api</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies> </project>2、工程結(jié)構(gòu)
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-CvhF8cmR-1644219205107)(data:image/svg+xml;utf8, )]
3、配置文件
在resources中創(chuàng)建application.properties
spring.application.name=order-service server.port=53021 spring.main.allow-bean-definition-overriding = true logging.level.root = debug logging.level.org.springframework.web = info spring.http.encoding.enabled = true spring.http.encoding.charset = UTF-8 spring.http.encoding.force = true server.tomcat.remote_ip_header = x-forwarded-for server.tomcat.protocol_header = x-forwarded-proto server.use-forward-headers = true server.servlet.context-path = /order spring.freemarker.enabled = true spring.freemarker.suffix = .html spring.freemarker.request-context-attribute = rc spring.freemarker.content-type = text/html spring.freemarker.charset = UTF-8 spring.mvc.throw-exception-if-no-handler-found = true spring.resources.add-mappings = false #eureka.client.serviceUrl.defaultZone = http://localhost:53000/eureka/ #eureka.instance.preferIpAddress = true #eureka.instance.instance-id = ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}} management.endpoints.web.exposure.include = refresh,health,info,env feign.hystrix.enabled = true feign.compression.request.enabled = true feign.compression.request.mime-types[0] = text/xml feign.compression.request.mime-types[1] = application/xml feign.compression.request.mime-types[2] = application/json feign.compression.request.min-request-size = 2048 feign.compression.response.enabled = true4.2.2.授權(quán)服務(wù)器配置
4.2.2.1 EnableAuthorizationServer
可以用@EnableAuthorizationServer注解并繼承AuthorizationServerConfigurerAdapter來配置OAuth2.0 授權(quán)服務(wù)器。
在Config包下創(chuàng)建AuthorizationServer:
@Configuration @EnableAuthorizationServer public class AuthorizationServer extendsAuthorizationServerConfigurerAdapter {//略...}AuthorizationServerConfigurerAdapter要求配置以下幾個(gè)類,這幾個(gè)類是由Spring創(chuàng)建的獨(dú)立的配置對象,它們會(huì)被Spring傳入AuthorizationServerConfigurer中進(jìn)行配置。
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {public AuthorizationServerConfigurerAdapter() {}public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}public void configure(ClientDetailsServiceConfigurer clients) throws Exception {}public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {} }-
ClientDetailsServiceConfigurer :用來配置客戶端詳情服務(wù)(ClientDetailsService),客戶端詳情信息在這里進(jìn)行初始化,你能夠把客戶端詳情信息寫死在這里或者是通過數(shù)據(jù)庫來存儲(chǔ)調(diào)取詳情信息。
-
AuthorizationServerEndpointsConfigurer :用來配置令牌(token)的訪問端點(diǎn)和令牌服務(wù)(token services)。
-
AuthorizationServerSecurityConfigurer :用來配置令牌端點(diǎn)的安全約束.
4.2.2.1.配置客戶端詳細(xì)信息
ClientDetailsServiceConfigurer 能夠使用內(nèi)存或者JDBC來實(shí)現(xiàn)客戶端詳情服務(wù)(ClientDetailsService),ClientDetailsService負(fù)責(zé)查找ClientDetails,而ClientDetails有幾個(gè)重要的屬性如下列表:
- clientId :(必須的)用來標(biāo)識客戶的Id。
- secret :(需要值得信任的客戶端)客戶端安全碼,如果有的話。
- scope :用來限制客戶端的訪問范圍,如果為空(默認(rèn))的話,那么客戶端擁有全部的訪問范圍。
- authorizedGrantTypes :此客戶端可以使用的授權(quán)類型,默認(rèn)為空。
- authorities :此客戶端可以使用的權(quán)限(基于Spring Security authorities)。
客戶端詳情(Client Details)能夠在應(yīng)用程序運(yùn)行的時(shí)候進(jìn)行更新,可以通過訪問底層的存儲(chǔ)服務(wù)(例如將客戶端詳情存儲(chǔ)在一個(gè)關(guān)系數(shù)據(jù)庫的表中,就可以使用 JdbcClientDetailsService)或者通過自己實(shí)現(xiàn)ClientRegistrationService接口(同時(shí)你也可以實(shí)現(xiàn) ClientDetailsService 接口)來進(jìn)行管理。
我們暫時(shí)使用內(nèi)存方式存儲(chǔ)客戶端詳情信息,配置如下:
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // clients.withClientDetails(clientDetailsService); clients.inMemory()// 使用in‐memory存儲(chǔ).withClient("c1")// client_id.secret(new BCryptPasswordEncoder().encode("secret")).resourceIds("res1").authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")// 該client允許的授權(quán)類型authorization_code,password,refresh_token,implicit,client_credentials.scopes("all")// 允許的授權(quán)范圍.autoApprove(false)//加上驗(yàn)證回調(diào)地址.redirectUris("http://www.baidu.com"); }4.2.2.2.管理令牌
AuthorizationServerTokenServices 接口定義了一些操作使得你可以對令牌進(jìn)行一些必要的管理,令牌可以被用來加載身份信息,里面包含了這個(gè)令牌的相關(guān)權(quán)限。
自己可以創(chuàng)建 AuthorizationServerTokenServices 這個(gè)接口的實(shí)現(xiàn),則需要繼承 DefaultTokenServices 這個(gè)類,里面包含了一些有用實(shí)現(xiàn),你可以使用它來修改令牌的格式和令牌的存儲(chǔ)。默認(rèn)的,當(dāng)它嘗試創(chuàng)建一個(gè)令牌的時(shí)候,是使用隨機(jī)值來進(jìn)行填充的,除了持久化令牌是委托一個(gè) TokenStore 接口來實(shí)現(xiàn)以外,這個(gè)類幾乎幫你做了所有的事情。并且 TokenStore 這個(gè)接口有一個(gè)默認(rèn)的實(shí)現(xiàn),它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了內(nèi)存中。除了使用這個(gè)類以外,你還可以使用一些其他的預(yù)定義實(shí)現(xiàn),下面有幾個(gè)版本,它們都實(shí)現(xiàn)了TokenStore接口:
- InMemoryTokenStore :這個(gè)版本的實(shí)現(xiàn)是被默認(rèn)采用的,它可以完美的工作在單服務(wù)器上(即訪問并發(fā)量壓力不大的情況下,并且它在失敗的時(shí)候不會(huì)進(jìn)行備份),大多數(shù)的項(xiàng)目都可以使用這個(gè)版本的實(shí)現(xiàn)來進(jìn)行嘗試,你可以在開發(fā)的時(shí)候使用它來進(jìn)行管理,因?yàn)椴粫?huì)被保存到磁盤中,所以更易于調(diào)試。
- JdbcTokenStore :這是一個(gè)基于JDBC的實(shí)現(xiàn)版本,令牌會(huì)被保存進(jìn)關(guān)系型數(shù)據(jù)庫。使用這個(gè)版本的實(shí)現(xiàn)時(shí),你可以在不同的服務(wù)器之間共享令牌信息,使用這個(gè)版本的時(shí)候請注意把"spring-jdbc"這個(gè)依賴加入到你的classpath當(dāng)中。
- JwtTokenStore :這個(gè)版本的全稱是 JSON Web Token(JWT),它可以把令牌相關(guān)的數(shù)據(jù)進(jìn)行編碼(因此對于后端服務(wù)來說,它不需要進(jìn)行存儲(chǔ),這將是一個(gè)重大優(yōu)勢),但是它有一個(gè)缺點(diǎn),那就是撤銷一個(gè)已經(jīng)授權(quán)令牌將會(huì)非常困難,所以它通常用來處理一個(gè)生命周期較短的令牌以及撤銷刷新令牌(refresh_token)。另外一個(gè)缺點(diǎn)就是這個(gè)令牌占用的空間會(huì)比較大,如果你加入了比較多用戶憑證信息。JwtTokenStore 不會(huì)保存任何數(shù)據(jù),但是它在轉(zhuǎn)換令牌值以及授權(quán)信息方面與 DefaultTokenServices 所扮演的角色是一樣的。
1、定義TokenConfig
在config包下定義TokenConfig,我們暫時(shí)先使用InMemoryTokenStore,生成一個(gè)普通的令牌。
@Configuration public class TokenConfig {@Beanpublic TokenStore tokenStore() {return new InMemoryTokenStore();} }2、定義AuthorizationServerTokenServices在AuthorizationServer中定義AuthorizationServerTokenServices
@Autowired private TokenStore tokenStore;@Autowiredprivate ClientDetailsService clientDetailsService;@Beanpublic AuthorizationServerTokenServices tokenService() {DefaultTokenServices service=new DefaultTokenServices();service.setClientDetailsService(clientDetailsService);service.setSupportRefreshToken(true);service.setTokenStore(tokenStore);service.setAccessTokenValiditySeconds(7200); // 令牌默認(rèn)有效期2小時(shí)service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默認(rèn)有效期3天return service;}4.2.2.3.令牌訪問端點(diǎn)配置
AuthorizationServerEndpointsConfigurer 這個(gè)對象的實(shí)例可以完成令牌服務(wù)以及令牌endpoint配置。
配置授權(quán)類型(Grant Types)
AuthorizationServerEndpointsConfigurer 通過設(shè)定以下屬性決定支持的授權(quán)類型(Grant Types):
- authenticationManager :認(rèn)證管理器,當(dāng)你選擇了資源所有者密碼(password)授權(quán)類型的時(shí)候,請?jiān)O(shè)置這個(gè)屬性注入一個(gè) AuthenticationManager 對象。
- userDetailsService :如果你設(shè)置了這個(gè)屬性的話,那說明你有一個(gè)自己的 UserDetailsService 接口的實(shí)現(xiàn),或者你可以把這個(gè)東西設(shè)置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 這個(gè)配置對象),當(dāng)你設(shè)置了這個(gè)之后,那么 “refresh_token” 即刷新令牌授權(quán)類型模式的流程中就會(huì)包含一個(gè)檢查,用來確保這個(gè)賬號是否仍然有效,假如說你禁用了這個(gè)賬戶的話。
- authorizationCodeServices :這個(gè)屬性是用來設(shè)置授權(quán)碼服務(wù)的(即 AuthorizationCodeServices 的實(shí)例對象),主要用于 “authorization_code” 授權(quán)碼類型模式。
- implicitGrantService :這個(gè)屬性用于設(shè)置隱式授權(quán)模式,用來管理隱式授權(quán)模式的狀態(tài)。
- tokenGranter :當(dāng)你設(shè)置了這個(gè)東西(即 TokenGranter 接口實(shí)現(xiàn)),那么授權(quán)將會(huì)交由你來完全掌控,并且會(huì)忽略掉上面的這幾個(gè)屬性,這個(gè)屬性一般是用作拓展用途的,即標(biāo)準(zhǔn)的四種授權(quán)模式已經(jīng)滿足不了你的需求的時(shí)候,才會(huì)考慮使用這個(gè)。
配置授權(quán)端點(diǎn)的URL(Endpoint URLs):
AuthorizationServerEndpointsConfigurer 這個(gè)配置對象有一個(gè)叫做 pathMapping() 的方法用來配置端點(diǎn)URL鏈接,它有兩個(gè)參數(shù):
- 第一個(gè)參數(shù): String 類型的,這個(gè)端點(diǎn)URL的默認(rèn)鏈接。
- 第二個(gè)參數(shù): String 類型的,你要進(jìn)行替代的URL鏈接。
以上的參數(shù)都將以 “/” 字符為開始的字符串,框架的默認(rèn)URL鏈接如下列表,可以作為這個(gè) pathMapping() 方法的第一個(gè)參數(shù):
- /oauth/authorize :授權(quán)端點(diǎn)。
- /oauth/token :令牌端點(diǎn)。
- /oauth/confirm_access :用戶確認(rèn)授權(quán)提交端點(diǎn)。
- /oauth/error :授權(quán)服務(wù)錯(cuò)誤信息端點(diǎn)。
- /oauth/check_token :用于資源服務(wù)訪問的令牌解析端點(diǎn)。
- /oauth/token_key :提供公有密匙的端點(diǎn),如果你使用JWT令牌的話。
需要注意的是授權(quán)端點(diǎn)這個(gè)URL應(yīng)該被Spring Security保護(hù)起來只供授權(quán)用戶訪問.
在AuthorizationServer配置令牌訪問端點(diǎn)
@Autowired private AuthorizationCodeServices authorizationCodeServices;@Autowiredprivate AuthenticationManager authenticationManager;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {endpoints.authenticationManager(authenticationManager).authorizationCodeServices(authorizationCodeServices).tokenServices(tokenService()).allowedTokenEndpointRequestMethods(HttpMethod.POST);}@Beanpublic AuthorizationCodeServices authorizationCodeServices() { //設(shè)置授權(quán)碼模式的授權(quán)碼如何存取,暫時(shí)采用內(nèi)存方式return new InMemoryAuthorizationCodeServices();}因?yàn)榕渲昧钆圃L問端點(diǎn)需要認(rèn)證管理器,也要配置
4.2.2.4.令牌端點(diǎn)的安全約束
**AuthorizationServerSecurityConfigurer :**用來配置令牌端點(diǎn)(Token Endpoint)的安全約束,在AuthorizationServer中配置如下.
@Override public void configure(AuthorizationServerSecurityConfigurer security){security.tokenKeyAccess("permitAll()") //(1) .checkTokenAccess("permitAll()") //(2) .allowFormAuthenticationForClients() //(3) ; }(1)tokenkey這個(gè)endpoint當(dāng)使用JwtToken且使用非對稱加密時(shí),資源服務(wù)用于獲取公鑰而開放的,這里指這個(gè)endpoint完全公開。
(2)checkToken這個(gè)endpoint完全公開
(3) 允許表單認(rèn)證
授權(quán)服務(wù)配置總結(jié):授權(quán)服務(wù)配置分成三大塊,可以關(guān)聯(lián)記憶。
既然要完成認(rèn)證,它首先得知道客戶端信息從哪兒讀取,因此要進(jìn)行客戶端詳情配置。
既然要頒發(fā)token,那必須得定義token的相關(guān)endpoint,以及token如何存取,以及客戶端支持哪些類型的token。
既然暴露除了一些endpoint,那對這些endpoint可以定義一些安全上的約束等。
4.2.2.5 web安全配置
將Spring-Boot工程中的WebSecurityConfig拷貝到UAA工程中。
@Configuration @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}//安全攔截機(jī)制(最重要)@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/r/r1").hasAnyAuthority("p1").antMatchers("/login*").permitAll().anyRequest().authenticated().and() .formLogin();} }4.2.3.授權(quán)碼模式
4.2.3.1 授權(quán)碼模式介紹
下圖是授權(quán)碼模式交互圖:
(1)資源擁有者打開客戶端,客戶端要求資源擁有者給予授權(quán),它將瀏覽器被重定向到授權(quán)服務(wù)器,重定向時(shí)會(huì)附加客戶端的身份信息。如:(瀏覽器輸入以下)
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com參數(shù)列表如下:
- client_id :客戶端準(zhǔn)入標(biāo)識。
- response_type :授權(quán)碼模式固定為code。
- scope :客戶端權(quán)限。
- redirect_uri :跳轉(zhuǎn)uri,當(dāng)授權(quán)碼申請成功后會(huì)跳轉(zhuǎn)到此地址,并在后邊帶上code參數(shù)(授權(quán)碼)。
(2)瀏覽器出現(xiàn)向授權(quán)服務(wù)器授權(quán)頁面,之后將用戶同意授權(quán)。
(3)授權(quán)服務(wù)器將授權(quán)碼(AuthorizationCode)轉(zhuǎn)經(jīng)瀏覽器發(fā)送給client(通過redirect_uri)。
(4)客戶端拿著授權(quán)碼向授權(quán)服務(wù)器索要訪問access_token,請求如下:
http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://www.baidu.com參數(shù)列表如下
- client_id :客戶端準(zhǔn)入標(biāo)識。
- client_secret :客戶端秘鑰。
- grant_type :授權(quán)類型,填寫authorization_code,表示授權(quán)碼模式
- code :授權(quán)碼,就是剛剛獲取的授權(quán)碼,注意:授權(quán)碼只使用一次就無效了,需要重新申請。
- redirect_uri :申請授權(quán)碼時(shí)的跳轉(zhuǎn)url,一定和申請授權(quán)碼時(shí)用的redirect_uri一致。
(5)授權(quán)服務(wù)器返回令牌(access_token)
這種模式是四種模式中最安全的一種模式。一般用于client是Web服務(wù)器端應(yīng)用或第三方的原生App調(diào)用資源服務(wù)的時(shí)候。因?yàn)樵谶@種模式中access_token不會(huì)經(jīng)過瀏覽器或移動(dòng)端的App,而是直接從服務(wù)端去交換,這樣就最大限度的減小了令牌泄漏的風(fēng)險(xiǎn)。
4.2.3.2 測試
瀏覽器訪問認(rèn)證頁面:
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com然后輸入模擬的賬號和密碼點(diǎn)登陸之后進(jìn)入授權(quán)頁面:
確認(rèn)授權(quán)后,瀏覽器會(huì)重定向到指定路徑(oauth_client_details表中的web_server_redirect_uri)并附加驗(yàn)證碼?code=sc0N9W(每次不一樣),最后使用該驗(yàn)證碼獲取token。
POST http://localhost:53020/uaa/oauth/token4.2.4. 簡化模式
4.2.4.1 簡化模式介紹
下圖是簡化模式交互圖:
(1)資源擁有者打開客戶端,客戶端要求資源擁有者給予授權(quán),它將瀏覽器被重定向到授權(quán)服務(wù)器,重定向時(shí)會(huì)附加客戶端的身份信息。如:
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com參數(shù)描述同授權(quán)碼模式 ,注意response_type=token,說明是簡化模式。
(2)瀏覽器出現(xiàn)向授權(quán)服務(wù)器授權(quán)頁面,之后將用戶同意授權(quán)。
(3)授權(quán)服務(wù)器將授權(quán)碼將令牌(access_token)以Hash的形式存放在重定向uri的fargment中發(fā)送給瀏覽器。
注:fragment 主要是用來標(biāo)識 URI 所標(biāo)識資源里的某個(gè)資源,在 URI 的末尾通過 (#)作為 fragment 的開頭,其中 # 不屬于 fragment 的值。如https://domain/index#L18這個(gè) URI 中 L18 就是 fragment 的值。大家只需要知道js通過響應(yīng)瀏覽器地址欄變化的方式能獲取到fragment 就行了。
一般來說,簡化模式用于沒有服務(wù)器端的第三方單頁面應(yīng)用,因?yàn)闆]有服務(wù)器端就無法接收授權(quán)碼。
6.2.4.2 測試
瀏覽器訪問認(rèn)證頁面:
http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com然后輸入模擬的賬號和密碼點(diǎn)登陸之后進(jìn)入授權(quán)頁面:
確認(rèn)授權(quán)后,瀏覽器會(huì)重定向到指定路徑(oauth_client_details表中的web_server_redirect_uri)并以Hash的形式存放在重定向uri的fargment中,如:
http://www.baidu.com/receive#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbn...4.2.5.密碼模式
4.2.5.1 授權(quán)碼模式介紹
下圖是密碼模式交互圖:
(1)資源擁有者將用戶名、密碼發(fā)送給客戶端
(2)客戶端拿著資源擁有者的用戶名、密碼向授權(quán)服務(wù)器請求令牌(access_token),請求如下:
http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=shangsan&password=123參數(shù)列表如下:
- client_id :客戶端準(zhǔn)入標(biāo)識。
- client_secret :客戶端秘鑰。
- grant_type :授權(quán)類型,填寫password表示密碼模式
- username :資源擁有者用戶名。
- password :資源擁有者密碼。
(3)授權(quán)服務(wù)器將令牌(access_token)發(fā)送給client
這種模式十分簡單,但是卻意味著直接將用戶敏感信息泄漏給了client,因此這就說明這種模式只能用于client是我們自己開發(fā)的情況下。因此密碼模式一般用于我們自己開發(fā)的,第一方原生App或第一方單頁面應(yīng)用。
4.2.5.2 測試
POST http://localhost:53020/uaa/oauth/token請求參數(shù):
返回結(jié)果:
4.2.6.客戶端模式
4.2.6.1 客戶端模式介紹
(1)客戶端向授權(quán)服務(wù)器發(fā)送自己的身份信息,并請求令牌(access_token)
(2)確認(rèn)客戶端身份無誤后,將令牌(access_token)發(fā)送給client,請求如下:
http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials參數(shù)列表如下:
- client_id :客戶端準(zhǔn)入標(biāo)識。
- client_secret :客戶端秘鑰。
- grant_type :授權(quán)類型,填寫client_credentials表示客戶端模式
這種模式是最方便但最不安全的模式。因此這就要求我們對client完全的信任,而client本身也是安全的。因此這種模式一般用來提供給我們完全信任的服務(wù)器端服務(wù)。比如,合作方系統(tǒng)對接,拉取一組用戶信息。
6.2.6.2 客戶端模式介紹
POST http://localhost:53020/uaa/oauth/token請求參數(shù):
返回結(jié)果:
4.2.7.資源服務(wù)測試
4.2.7.1 資源服務(wù)器配置
@EnableResourceServer 注解到一個(gè) @Configuration 配置類上,并且必須使用 ResourceServerConfigurer 這個(gè)配置對象來進(jìn)行配置(可以選擇繼承自 ResourceServerConfigurerAdapter 然后覆寫其中的方法,參數(shù)就是這個(gè)
對象的實(shí)例),下面是一些可以配置的屬性:
ResourceServerSecurityConfigurer中主要包括:
- tokenServices :ResourceServerTokenServices 類的實(shí)例,用來實(shí)現(xiàn)令牌服務(wù)。
- tokenStore :TokenStore類的實(shí)例,指定令牌如何訪問,與tokenServices配置可選
- resourceId :這個(gè)資源服務(wù)的ID,這個(gè)屬性是可選的,但是推薦設(shè)置并在授權(quán)服務(wù)中進(jìn)行驗(yàn)證。
- 其他的拓展屬性例如 tokenExtractor 令牌提取器用來提取請求中的令牌。
HttpSecurity配置這個(gè)與Spring Security類似:
- 請求匹配器,用來設(shè)置需要進(jìn)行保護(hù)的資源路徑,默認(rèn)的情況下是保護(hù)資源服務(wù)的全部路徑。
- 通過 http.authorizeRequests()來設(shè)置受保護(hù)資源的訪問規(guī)則
- 其他的自定義權(quán)限保護(hù)規(guī)則通過 HttpSecurity 來進(jìn)行配置。
@EnableResourceServer 注解自動(dòng)增加了一個(gè)類型為 OAuth2AuthenticationProcessingFilter 的過濾器鏈
編寫ResouceServerConfig:
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResouceServerConfig extends ResourceServerConfigurerAdapter {public static final String RESOURCE_ID = "res1";@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId(RESOURCE_ID).tokenServices(tokenService()).stateless(true);}@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/**").access("#oauth2.hasScope('all')").and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);} }4.2.7.2 驗(yàn)證token
ResourceServerTokenServices 是組成授權(quán)服務(wù)的另一半,如果你的授權(quán)服務(wù)和資源服務(wù)在同一個(gè)應(yīng)用程序上的話,你可以使用 DefaultTokenServices ,這樣的話,你就不用考慮關(guān)于實(shí)現(xiàn)所有必要的接口的一致性問題。如果你的資源服務(wù)器是分離開的,那么你就必須要確保能夠有匹配授權(quán)服務(wù)提供的 ResourceServerTokenServices,它知道如何對令牌進(jìn)行解碼。
令牌解析方法: 使用 DefaultTokenServices 在資源服務(wù)器本地配置令牌存儲(chǔ)、解碼、解析方式 使用RemoteTokenServices 資源服務(wù)器通過 HTTP 請求來解碼令牌,每次都請求授權(quán)服務(wù)器端點(diǎn) /oauth/check_token
使用授權(quán)服務(wù)的 /oauth/check_token 端點(diǎn)你需要在授權(quán)服務(wù)將這個(gè)端點(diǎn)暴露出去,以便資源服務(wù)可以進(jìn)行訪問,這在咱們授權(quán)服務(wù)配置中已經(jīng)提到了,下面是一個(gè)例子,在這個(gè)例子中,我們在授權(quán)服務(wù)中配置了/oauth/check_token 和 /oauth/token_key 這兩個(gè)端點(diǎn):
@Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security .tokenKeyAccess("permitAll()")// /oauth/token_key 安全配置 .checkTokenAccess("permitAll()") // /oauth/check_token 安全配置 }在資源 服務(wù)配置RemoteTokenServices ,在ResouceServerConfig中配置:
// 資源服務(wù)令牌解析服務(wù) @Bean public ResourceServerTokenServices tokenService() {//使用遠(yuǎn)程服務(wù)請求授權(quán)服務(wù)器校驗(yàn)token,必須指定校驗(yàn)token 的url、client_id,client_secretRemoteTokenServices service=new RemoteTokenServices();service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");service.setClientId("c1");service.setClientSecret("secret");return service; }4.2.7.3 編寫資源
在controller包下編寫OrderController,此controller表示訂單資源的訪問類:
@RestController public class OrderController {@GetMapping(value = "/r1")@PreAuthorize("hasAnyAuthority('p1')")public String r1(){return "訪問資源1";} }4.2.7.4 添加安全訪問控制
@Configuration @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//安全攔截機(jī)制(最重要)@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests() // .antMatchers("/r/r1").hasAuthority("p2") // .antMatchers("/r/r2").hasAuthority("p2").antMatchers("/r/**").authenticated()//所有/r/**的請求必須認(rèn)證通過.anyRequest().permitAll()//除了/r/**,其它的請求可以訪問;} }配置引導(dǎo)類OrderServer
@SpringBootApplication public class OrderServer {public static void main(String[] args) {SpringApplication.run(OrderServer.class, args);} }4.2.7.5 測試
1、申請令牌
這里我們使用密碼方式
2、請求資源
按照oauth2.0協(xié)議要求,請求資源需要攜帶token,如下:
token的參數(shù)名稱為:Authorization,值為:Bearer token值
如果token錯(cuò)誤,則授權(quán)失敗,如下:
總結(jié)
以上是生活随笔為你收集整理的Spring Security OAuth2分布式系统认证解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《一周学完光线追踪》学习 三 光线相机和
- 下一篇: SpringSecurity最全实战讲解