第十八章:SpringBoot項目中使用SpringSecurity整合OAuth2設計項目API安全接口服務

OAuth是一個關於受權的開放網絡標準,在全世界獲得的普遍的應用,目前是2.0的版本。OAuth2在「客戶端」與「服務提供商」之間,設置了一個受權層(authorization layer)。「客戶端」不能直接登陸「服務提供商」,只能登陸受權層,以此將用戶與客戶端分離。「客戶端」登陸須要OAuth提供的令牌,不然將提示認證失敗而致使客戶端沒法訪問服務。下面咱們就來說解下SpringBoot項目中是如何配置使用OAuth2服務器端,並讓OAuth2整合SpringSecurity來保護咱們的REST接口。git


知識星球

已開通知識星球,歡迎你們加入交流技術以及提問SpringBoot相關問題。spring


本章目標

基於SpringBoot項目提供一個繼承OAuth2安全框架的REST API服務端,必須獲取訪問受權令牌後才能夠訪問資源。數據庫

OAuth2受權方式

咱們在文章開始已經說過了,咱們的保護資源必須經過受權獲得的令牌才能夠訪問。那麼咱們這個受權令牌要經過什麼方式獲取呢?瀏覽器

OAuth2爲咱們提供了四種受權方式:安全

一、受權碼模式(authorization code)
二、簡化模式(implicit)
三、密碼模式(resource owner password credentials)
四、客戶端模式(client credentials)服務器

受權碼模式

受權碼相對其餘三種來講是功能比較完整、流程最安全嚴謹的受權方式,經過客戶端的後臺服務器與服務提供商的認證服務器交互來完成。流程以下圖2所示:網絡


圖2

簡化模式

這種模式不經過服務器端程序來完成,直接由瀏覽器發送請求獲取令牌,令牌是徹底暴露在瀏覽器中的,這種模式極力不推崇。流程以下圖3所示:session


圖3

密碼模式

密碼模式也是比較經常使用到的一種,客戶端向受權服務器提供用戶名、密碼而後獲得受權令牌。這種模式不過有種弊端,咱們的客戶端須要存儲用戶輸入的密碼,可是對於用戶來講信任度不高的平臺是不可能讓他們輸入密碼的。流程以下圖4所示:app


圖4

客戶端模式

客戶端模式是客戶端以本身的名義去受權服務器申請受權令牌,並非徹底意義上的受權。以下圖5所示:框架


圖5

上述簡單的介紹了OAuth2內部的四種受權方式,咱們下面使用密碼模式來進行測試,而且咱們使用數據庫中的用戶數據來作驗證處理,下面咱們先來構建項目。

構建項目

咱們使用IndeiiJ IDEA工具來構建一個SpringBoot項目,目前最新版本的是1.5.3,應該是昨天剛正式發佈。項目咱們預先引入幾個模塊,Web、JPA、MySQL、Security、SpringSecurityOAuth二、Druid等,項目結構以下圖6所示:


圖6

項目構建完成後咱們要配置數據庫表結構,由於咱們要是數據庫內保存AccessToken以及RefershToken還有咱們的SpringSecurity用戶驗證信息以及用戶角色信息等。

配置數據庫

安全用戶信息表

用戶信息表包含了簡單的登陸名、密碼、郵箱、狀態等。表結構以下圖7所示:


圖7

安全角色信息表

角色信息表結構以下圖8所示:


圖8

用戶角色關聯表

用戶與角色關聯表結構以下圖9所示:


圖9

AccessToken信息表

咱們使用的是SpringSecurityOAuth2提供的Jdbc方式進行操做Token,因此須要根據標準建立對應的表結構,access_token信息表結構以下圖10所示:


圖10

RefreshToken信息表

刷新Token時須要用到refresh_token信息表結構以下圖11所示:


圖11

咱們的數據庫表結構已經建完了,下面咱們只須要建立用戶信息、角色信息的實體便可,由於OAuth2內部操做數據庫使用的JdbcTemplate咱們只須要傳入一個DataSource對象就能夠了,實體並不須要配置。

建立用戶實體

用戶實體以下圖12所示:


圖12

建立角色實體

角色實體以下圖13所示:


圖13

用戶實體以及角色實體是用來配置SpringSecurity時用到的實體,咱們配置SpringSecurity時須要使用SpringDataJPA從數據庫中讀取數據,下咱們來配置UserJPA以及AuthorityJPA。

UserJPA

配置訪問數據庫獲取用戶信息,代碼以下圖14所示:


圖14

咱們在UserJPA內添加了一個自定義查詢,使用了HQL語法來構建的語句,根據用戶名不區分大小寫進行查詢。

Application.yml配置文件

咱們從以前的項目中第十三章:SpringBoot實戰SpringDataJPA中源碼複製一個application.yml配置文件到項目resources下(注意:須要修改對應的數據庫配置),以下圖所示:


.

AuthorityJPA

配置訪問數據庫中的角色列表,代碼以下圖15所示:


圖15

下面咱們來配置兩個控制器用來區分咱們配置OAuth2是否已經生效。

HelloWorldController

我在HelloWorldController內只添加一個字符串的輸出,這個控制器咱們開放,讓SpringSecurity不去管理,配置將會在下面展示,控制器代碼以下圖16所示:


圖16

SecureController

這個控制器是須要咱們獲取受權Token後使用Token才能夠訪問到的,代碼以下圖17所示:


圖17

綜上所述咱們的項目基礎的構建已經完成,你們都知道SpringSecurity在使用數據庫的數據時須要自定義UserDetailsService用來從數據庫中根據用戶名查詢用戶信息以及角色信息並返回給SpringSecurity存放到內存中。

自定義UserDetailsService

咱們建立一個名叫HengYuUserDetailsService的類而且實現UserDetailsService接口,代碼以下圖18所示:


圖18

咱們在HengYuUserDetailsService類中作了從數據庫讀取用戶的操做,若是沒有查詢到用戶直接拋出異常提示,若是查詢到而且設置對應的角色後返回SpringSecurity內置的User對象實例。

開啓SpringSecurity配置

下面咱們來配置SpringSecurity相關的內容,咱們新建立一個配置類SecurityConfiguration,代碼以下圖19所示:


圖19

咱們在配置類中注入了上面咱們自定義的HengYuUserDetailsService以及用戶密碼驗證規則,咱們使用ignoring()方法排除了HelloWorldController內的公開方法,這裏能夠配置通配符的形式排除。

配置安全資源服務器

下面咱們開始配置相關OAuth2的內容,咱們建立一個OAuth2總配置類OAuth2Configuration,類內添加一個子類用於配置資源服務器,以下圖20所示:


圖20

咱們在OAuth2Configuration配置類中添加子類ResourceServerConfiguration繼承自ResourceServerConfigurerAdapter完成資源服務器的配置,使用@EnableResourceServer註解來開啓資源服務器,由於整合SpringSecurity的緣故,咱們須要配置登出時清空對應的access_token控制以及自定義401錯誤內容(authenticationEntryPoint),在配置類中咱們排除了對/hello公開地址攔截以及/secure下的全部地址都必須受權才能夠訪問。

自定義401錯誤碼內容

咱們上圖已經用到了對應的類CustomAuthenticationEntryPoint,該類是用來配置若是沒有權限訪問接口時咱們返回的錯誤碼以及錯誤內容,代碼以下圖21所示:


圖21

定義登出控制

當咱們退出系統時須要訪問SpringSecrutiy的logout方法來清空對應的session信息,那咱們退出後改用戶的access_token還依然存在那就危險了,一旦別人知道該token就可使用以前登陸用戶的權限來操做業務。logout控制代碼以下圖22所示:


圖22

開啓OAuth2驗證服務器

咱們仍是在OAuth2Configuration配置類中添加一個子類,用於開啓OAuth2的驗證服務器,代碼以下圖2三、24所示:


圖23

圖23中咱們建立了一個名叫AuthorizationServerConfiguration的類繼承自AuthorizationServerConfigurerAdapter而且實現了EnvironmentAware(讀取properties文件須要)接口,並使用@EnableAuthorizationServer註解開啓了驗證服務器,能夠看到咱們使用SpringSecurityOAuth2內定義的JdbcStore來操做數據庫中的Token,固然須要有須要咱們能夠經過SpringDataJPA自定義Sotre


圖24

圖24中咱們的OAuth2的客戶端配置並無從數據庫中讀取而是使用了內存中獲取,由於本章的內容比較多,因此在後期文章中咱們會再次講到如何從數據庫中獲取clients進行驗證。咱們在建立客戶端信息時使用到了application.properties配置文件的自定義配置,具體配置內容以下圖25所示:


圖25

運行測試

項目編寫完成,接下來咱們使用SpringBootApplication形式來運行項目進行測試,運行項目時查詢控制檯輸出日誌是否正確!

咱們先來使用Postman工具訪問一下咱們公開的地址127.0.0.1:8080/hello,以下圖26所示:


圖26

能夠看到咱們是能夠正確的訪問到接口輸出內容的,下面咱們再來訪問一下被oauth2管理的地址127.0.0.1:8080/secure,以下圖27所示:


圖27

咱們能夠看到直接給咱們返回了一個頁面,這樣就不對了,咱們應該獲得一個401的錯誤碼以及自定義的信息纔對,固然咱們須要添加一些配置來完成這個功能,咱們打開application.properties配置文件添加以下圖28配置:


圖28

圖中畫紅色框的就是咱們新添加的配置內容,這個配置的意思時,將咱們的資源攔截的過濾器運行順序放到第3個執行,也就是在oauth2的認證服務器後面執行,咱們重啓下項目再來訪問下剛纔的地址,輸出內容以下圖29所示:


圖29

能夠看到正如咱們預期同樣,返回了401錯誤以及咱們自定義的錯誤碼」Access Denied「,下面咱們來獲取access_token。

獲取AccessToken

咱們在獲取token以前須要在數據庫中添加幾條對應的數據,具體的SQL我會放到源碼項目的resources目錄下,文章地址有源碼地址。咱們來訪問/oauth/token地址獲取access_token,以下圖30所示:


圖30

能夠看到咱們訪問的地址,grant_type使用到了password模式,咱們在上面的配置中就是配置咱們的客戶端(yuqiyu_home_pc)能夠執行的模式有兩種:password、refresh_token。獲取access_token須要添加客戶端的受權信息clientid、secret,經過Postman工具的頭受權信息便可輸出對應的值就能夠完成Basic Auth的加密串生成。

成功訪問後oauth2給咱們返回了幾個參數:

access_token:本地訪問獲取到的access_token,會自動寫入到數據庫中。
token_type:獲取到的access_token的受權方式
refersh_token:刷新token時所用到的受權token
expires_in:有效期(從獲取開始計時,值秒後過時)
scope:客戶端的接口操做權限(read:讀,write:寫)

使用AccessToken訪問

咱們使用獲取到的access_token值來訪問對應的地址http://127.0.0.1:8080/secure?access_token=9ca7fd9b-1289-440b-b1a1-0303782f660e,效果以下圖31所示:


圖31

能夠看到咱們已經能夠正常的訪問到數據內容了,證實咱們的access_token是有效的。當咱們用到的token已通過期時效果以下圖32所示:


圖32

oauth2告訴咱們須要刷新Token了,您傳入的token值已通過期了。

刷新AccessToken

咱們的access_token過時咱們須要刷新後返回新的token,使用新token才能繼續操做數據接口。刷新access_token以下圖33所示:


圖33

看到上圖33紅色框內的值了嗎?這個就是咱們以前獲取token時,oauth2給咱們返回的refresh_token值,咱們須要用到該值來進行刷新token。新的token值得有效期能夠看到又是咱們配置的默認1800秒,刷新token時oauth2仍是給咱們返回了一個refersh_token值,該值要做爲下次刷新token時使用。

總結

綜上內容就是本章的所有內容,本章的內容比較多但願讀者能夠仔細閱讀,本章主要講解了SpringBoot做爲框架基礎上配置SpringSecurity安全框架整合OAuth2安全框架作雙重安全,講解若是經過數據庫的形式獲取到受權用戶信息以及角色列表,經過內存配置的OAuth2的客戶端配置來獲取access_token以及如何使用access_token訪問受保護的資源接口。

本章代碼已經上到碼雲:

SpringBoot配套源碼地址:gitee.com/hengboy/spr…

SpringCloud配套源碼地址:gitee.com/hengboy/spr…

SpringBoot相關係列文章請訪問:目錄:SpringBoot學習目錄

QueryDSL相關係列文章請訪問:QueryDSL通用查詢框架學習目錄

SpringDataJPA相關係列文章請訪問:目錄:SpringDataJPA學習目錄

SpringBoot相關文章請訪問:目錄:SpringBoot學習目錄,感謝閱讀!

歡迎加入QQ技術交流羣,共同進步。

相關文章
相關標籤/搜索