上一篇說了認證,通過令牌可以知道當前用戶是誰,並把令牌信息從網關到微服務,以及微服務與微服務之間傳遞用戶上下文的信息,這一篇來聊一下授權。
一、最簡單的情況ACL權限控制
用戶有哪些權限直接在scope里寫着,只要在程序里判斷一下要訪問某個方法,是否有訪問權限就可以了這種適用於權限簡單的場景。
使用 @PreAuthorize("") 注解標記在Controller方法,可以控制,哪些請求有權限訪問該服務。注意要想是該注解生效需要在啟動類上加上注解: @EnableGlobalMethodSecurity(prePostEnabled = true) 使其生效。
value值有兩種表達式:
1,@PreAuthorize("#oauth2.hasScope('fly')") 表示,scope包含“fly”的令牌,才可以訪問該服務。這個針對客戶端應用的,不能具體到某一個人。
2,@PreAuthorize("hasRole('ROLE_USER')") 具體到每一個人,這個人包含哪些角色,就能訪問,這個角色在認證服務器的 UserDetailsService 類的 loadUserByUsername 方法里獲取。
實驗@PreAuthorize("#oauth2.hasScope('fly')") :
用orderService 客戶端通過網關 ,獲取token
這個token的scope只包含 read,write ,但是創建訂單服務需要token的scope包含fly才能訪問,用這個token通過網關訪問創建訂單服務:
將創建訂單服務的權限控制表達式換成 @PreAuthorize("#oauth2.hasScope('write')") 就可以正常訪問。
實驗@PreAuthorize("#hasRole('ROLE_USER')") :
通過網關獲取令牌
創建訂單服務,有ROLE_USER角色,才能訪問
認證服務器的UserDetailsService ,寫死的權限,只有ROLE_ADMIN
通過網關拿token 創建訂單
權限控制注解換為 @PreAuthorize("hasRole('ROLE_ADMIN')") ,則可正常調用創建訂單服務。
使用@PreAuthorize 注解處理簡單角色很方便,但是沒辦法處理復雜的場景。如果權限總是變化的,這個就不適合了,因為權限控制是硬編碼在Controller了,每次修改權限信息,還得重啟服務。
二、在網關上做復雜的權限控制
假設已經有了一個權限系統,怎么跟網關接起來?
1,在 GatewaySecurityConfig 配置類上,需要做如下的修改
a) 指定權限訪問規則
在 GatewaySecurityConfig.configure(HttpSecurity http) 方法里,配置 http.access("#permissionService.hasPermission(request,authorization)") ,指定權限訪問規則,permissionService需要自己實現,返回布爾值,true-能訪問;false-無權限,傳進去2個參數,參數1-當前請求 ,參數2-當前用戶。
b) 新建 PermissionService 接口以及實現類 ,實現自己的權限控制邏輯。
c) 新建表達式處理 GatewayWebSecurityExpressionHandler
只指定這個權限訪問規則http.access("#permissionService.hasPermission(request,authorization)")是沒用的,因為Spring不認識,所以還得新建一個類:GatewayWebSecurityExpressionHandler (表達式處理器),用來往權限表達式處理器里設置變量,變量名就是 permissionService,變量值是
自定義的權限處理類 PermissionService
d)指定表達式處理器
GatewaySecurityConfig.configure(ResourceServerSecurityConfigurer resources)里 指定 resources.expressionHandler(gatewayWebSecurityExpressionHandler)表達式處理器

通過網關申請令牌,通過網關訪問創建訂單服務,有一半的可能性訪問失敗,一半可能性成功。
到目前為止的項目框架結構是這樣的:
應該是有一個權限服務的,權限服務也是一個微服務,認證服務+權限服務=安全中心。
目前的權限都在網關控制的,這樣可能存在越權:
比如你有權限訪問訂單服務,沒權限訪問庫存服務,但是訂單服務又調用了庫存服務,這樣就越權了。
怎么解決這個越權呢:
a) 可以在每個微服務上,都調用redis檢查權限,做法和上邊演示的在網關上的做法是一樣的,但是不建議這么做,所有微服務都依賴redis是大量的耦合。
b) 95%的細粒度的權限都在網關上做,微服務之期間互相調用的權限,只做一個粗粒度的黑白名單的控制,比如你一個結算服務,有一個白名單,只允許訂單服務來調用,其他人調不了。(后面介紹sentinel怎么方便的進行黑白名單的控制)。微服務之間的調用不用像網關那么做細粒度的控制,(這個服務你能不能調,那個服務你能不能調),但是要有一個黑白名單的控制就可以了,控制住那些微服務之間能調用就可以了。
代碼 :https://github.com/lhy1234/springcloud-security/tree/chapt-6-3-permission 如果幫到了你給個小星星吧
歡迎關注個人公眾號一起交流學習: