微服務API開放授權平台的設計與實現
- 2019 年 11 月 14 日
- 筆記
本文所介紹的項目是一個基於oath2協議的應用,實現的的功能邏輯與QQ互聯,微博開放平台類似,都是同一套認證授權流程。
項目結構簡單易懂,卻不偷工減料,在學習完本文內容後,讀者可以直接獲取文中的項目程式碼用於學習或者copy到公司的生產項目中修改後使用,真正達到學以致用的目的。
所涉及技術棧:
- java
- springboot 2.0.1.RELEASE
- spring security 2.0.1.RELEASE
- spring cloud oauth2 2.0.0.M7
- mybatis 2.0.1
本項目包括功能有
- 新用戶
- 用戶註冊自動分配角色許可權
- 用戶只能訪問自己所擁有的角色許可權訪問路徑
- 開放平台
- 用戶可以申請獲取客戶ID和客戶密鑰
- 用戶可以通過客戶ID獲取授權碼
- 用戶可以通過客戶ID和密鑰以及授權碼獲取access token 和referrsh token和scope
- 資源api服務(order-service/open-api-service)
- 可自定義配置需授權url
- 可自定義配置受限url的訪問scope
- 未授權用戶或訪問許可權不足用戶,頁面提示相資訊
- 用戶通過access token 來訪問對應url
項目概覽
首先來看一下項目的結構圖,了解項目的大致布局

- .idea:idea工具生成的文件
- docker: 存放Dockerfile文件用於構建容器鏡像
- config: 項目中關於spring security和web的配置
- controller:請求控制器
- entity: 對象實體類包
- handler:邏輯處理類包
- mapper:存放mybatis mapper介面類
- service: 業務邏輯處理類
- util: 通用的工具類
- postman:postman工具的測試用例
- sql:項目的初始化sql語句
- templates: 頁面模板文件
接下來正式介紹關於項目的細節,由於項目本身就已經有不少中文注釋,所以在講解的時候會收縮起一些程式碼的具體實現,如果讀者不習慣可以在這裡點在線比對閱讀或者fork到自己的項目里閱讀==> 項目程式碼
用戶是如何被攔截認證的

- 自定類 SecurityConfig 繼承了 WebSecurityConfigurerAdapter,說明SecurityConfig將會是一個 Http安全配置適配器
- 通過
@EnableWebSecurity打開了 HttpSecurity 的安全配置,則該類將生效

- 重寫 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法
- 指定哪裡請求url是不需要攔截直接放行的
- 指定哪些請求是需要攔截校驗的

- 重寫 WebSecurityConfigurerAdapter 的 configure(AuthenticationManagerBuilder auth) 方法
- 指定處理認證的service服務類為 MyUserDetailsService
- 比對輸入的密碼和資料庫中的用戶密碼是否一致

- 自定類 MyUserDetailsService 實現了 spring security的UserDetailsService 介面
- 重寫 loadUserByUsername(String username) 方法, 其中username是頁面傳的屬性和值,屬性是固定的
- 獲取用戶許可權表對應的許可權詳情,並把內容設置到 UserDetails.Authorities屬性中
- 返回 UserDetails的子類 User
用戶註冊自動分配角色許可權
用戶註冊則往用戶表插入數據,同時往用戶角色表也插入一份數據。 當然也可以設計的更複雜些,比如根據來源、時間、白名單、內部推薦等設置不同的許可權,讀者可自行擴展。

用戶只能訪問自己所擁有的角色許可權訪問路徑

- 取出當前用戶的所有許可權
- 取出許可權的許可權標識欄位,並包裝成一個List 集合
- 保存包裝的許可權list集合到 UserDetails 對象中

- 取出所有許可權表中內容
- http.authorizeRequests()獲取當前的認證對象
- 把許可權表中的內容全部設置到 authorizeRequests中, antMatchers表示攔截的url, hasAnyAuthority表示可以訪問的許可權標識
- 因為在上圖中user已經設置了自己所擁有許可權的許可權標識,所以可以訪問被攔截的url

用戶可以申請獲取客戶ID和客戶密鑰
首先來看一下表結構,oauth_client_details 為spring cloud oath2自帶的表, user_client_secret 為我們自己創建的表



- 生成 OauthClientDetails 數據並保存至資料庫中
- 獲取當前登錄人資訊,並綁定OauthClientDetails的clientId至user_client_secret表中
用戶可以通過客戶ID獲取授權碼
具體的實現在spring-security-oauth包中的, 非本項目內的自我實現
客戶ID獲取授權碼 請求url: http://localhost:8080/oauth/authorize?response_type=code&client_id=client_92&redirect_uri=http://localhost:8080/code


- 因為是要獲取授權碼,response_type=code為固定值
- 客戶申請的客戶ID
- oauth_client_details 表中的 web_server_redirect_uri
用授權碼獲取access_token
具體的實現在spring-security-oauth包中的 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint類,有興趣的同學可以在裡面進行debug調試
用授權碼獲取access_token 請求url: http://localhost:8080/oauth/token?grant_type=authorization_code&code=8fGtOV&client_id=client_92&client_secret=123456&redirect_uri=http://localhost:8080/code&scope=all

- 通過客戶ID獲取到的授權碼
- 客戶申請的客戶ID
- 客戶申請的客戶ID配套的客戶秘鑰
- oauth_client_details 表中的 web_server_redirect_uri
- oauth_client_details 表中的 scope

# 出現如下類似錯誤標識code失效,重新在獲取授權碼操作即可 { "error": "invalid_grant", "error_description": "Invalid authorization code: iq30f9" } 複製程式碼商戶id和商戶秘鑰獲取accessToken和刷新accessToken

- grant_type: 授權類型,這裡設置為密碼模式
- username: 用戶名
- password: 用戶密碼
- client_id: 申請得到的客戶Id
- client_secret: 申請得到的客戶秘鑰
- scope:範圍標識,取自 oauth_client_details 表中的 scope
刷新 accessToken
刷新 accessToken的 請求url: http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=4741d043-e202-4de0-ae21-4f5c7ec5626e&client_id=client_1&client_secret=123456

