1. 程式人生 > >第十八章:SpringBoot專案中使用SpringSecurity整合OAuth2設計專案API安全介面服務

第十八章:SpringBoot專案中使用SpringSecurity整合OAuth2設計專案API安全介面服務

OAuth是一個關於授權的開放網路標準,在全世界得到的廣泛的應用,目前是2.0的版本。OAuth2在“客戶端”與“服務提供商”之間,設定了一個授權層(authorization layer)。“客戶端”不能直接登入“服務提供商”,只能登入授權層,以此將使用者與客戶端分離。“客戶端”登入需要OAuth提供的令牌,否則將提示認證失敗而導致客戶端無法訪問服務。下面我們就來講解下SpringBoot專案中是如何配置使用OAuth2伺服器端,並讓OAuth2整合SpringSecurity來保護我們的REST介面。

本章目標

基於SpringBoot專案提供一個繼承OAuth2安全框架的REST API服務端,必須獲取訪問授權令牌後才可以訪問資源。

OAuth2授權方式

我們在文章開始已經說過了,我們的保護資源必須通過授權得到的令牌才可以訪問。那麼我們這個授權令牌要通過什麼方式獲取呢?

OAuth2為我們提供了四種授權方式:

1、授權碼模式(authorization code)
2、簡化模式(implicit)
3、密碼模式(resource owner password credentials)
4、客戶端模式(client credentials)

授權碼模式

授權碼相對其他三種來說是功能比較完整、流程最安全嚴謹的授權方式,通過客戶端的後臺伺服器與服務提供商的認證伺服器互動來完成。流程如下圖2所示:
圖2

簡化模式

這種模式不通過伺服器端程式來完成,直接由瀏覽器傳送請求獲取令牌,令牌是完全暴露在瀏覽器中的,這種模式極力不推崇。流程如下圖3所示:
圖3

密碼模式

密碼模式也是比較常用到的一種,客戶端向授權伺服器提供使用者名稱、密碼然後得到授權令牌。這種模式不過有種弊端,我們的客戶端需要儲存使用者輸入的密碼,但是對於使用者來說信任度不高的平臺是不可能讓他們輸入密碼的。流程如下圖4所示:
圖4

客戶端模式

客戶端模式是客戶端以自己的名義去授權伺服器申請授權令牌,並不是完全意義上的授權。如下圖5所示:
圖5

上述簡單的介紹了OAuth2內部的四種授權方式,我們下面使用密碼模式來進行測試,並且我們使用資料庫中的使用者資料來做驗證處理,下面我們先來構建專案。

構建專案

我們使用IndeiiJ IDEA工具來構建一個SpringBoot專案,目前最新版本的是1.5.3,應該是昨天剛正式釋出。專案我們預先引入幾個模組,Web、JPA、MySQL、Security、SpringSecurityOAuth2、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下(注意:需要修改對應的資料庫配置),如下圖所示:
application.yml

配置訪問資料庫中的角色列表,程式碼如下圖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的驗證伺服器,程式碼如下圖23、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是有效的。當我們用到的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訪問受保護的資源介面。

本章程式碼已經上到碼雲:

更多幹貨文章掃碼關注微信公眾號

掃碼關注 - 專注分享

加入知識星球,恆宇少年帶你走以後的技術道路!!!

微信掃碼加入