最新版 Spring Security,該如何實現動態許可權管理?
來源:江南一點雨
在松哥之前的教程中,曾經和小夥伴們聊過動態許可權管理的問題,大家在公眾號江南一點雨後臺回覆 s用不了在松哥之前的教程中,曾經和小夥伴們聊過動態許可權管理的問題,大家在公眾號江南一點雨後臺回覆 ss 有lss 有oDF 教程。
但是,不知道小夥伴們是否有留意過,進入到 Spring Boot3 之後,Spring Security 現在也進化到 Spring Security6 了,Spring Security6 的用法跟之前比起來還是有很大差異,松哥之前寫了篇文章和小夥伴們介紹 Spring Security6 中的一些變化:Spring Security6 全新寫法,大變樣!。
最近有小夥伴說松哥你之前講的動態許可權定義的方式,新版中用不了了,我抽空看了下,今天就和小夥伴們聊聊這個話題。
1. 許可權開發思路
先來說許可權開發的思路,當我們設計好 RBAC 許可權之後,具體到程式碼層面,我們有兩種實現思路:
直接在介面/Service 層方法上新增許可權註解,這樣做的好處是實現簡單,但是有一個問題就是許可權硬編碼,每一個方法需要什麼許可權都是程式碼中配置好的,後期如果想透過管理頁面修改是不可能的,要修改某一個方法所需要的許可權只能改程式碼。 將請求和許可權的關係透過資料庫來描述,每一個請求需要什麼許可權都在資料庫中配置好,當請求到達的時候,動態查詢,然後判斷許可權是否滿足,這樣做的好處是比較靈活,將來需要修改介面和許可權之間的關係時,可以透過管理頁面點選幾下,問題就解決了,不用修改程式碼,松哥之前的 vhr 中就是這樣做的。
有的小夥伴覺得第二種方案無法做到按鈕級別的許可權控制,這其實是一個誤解。想要做到按鈕級別的許可權控制,只需要資料庫中細化配置即可。
2. 具體實踐
2.1 舊方案回顧
在 vhr 中,松哥是透過重寫兩個類來和實現動態許可權的。
第一個類是收集許可權後設資料的類:
@Component
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
//...
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
在 getAttributes 方法中,根據當前請求的 URL 地址(從引數 Object 中可提取出來),然後根據許可權表中的配置,分析出來當前請求需要哪些許可權並返回。
另外我還重寫了一個決策器,其實決策器也可以不重寫,就看你自己的需求,如果 Spring Security 自帶的決策器無法滿足你的需求,那麼可以自己寫一個決策器:
@Component
public class CustomUrlDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
//...
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
decide 方法就是做決策的地方,第一個引數中可以提取出當前使用者具備什麼許可權,第三個引數是當前請求需要什麼許可權,比較一下就行了,如果當前使用者不具備需要的許可權,則直接丟擲 AccessDeniedException 異常即可。
最後,透過 Bean 的後置處理器 BeanPostProcessor,將這兩個配置類放到 Spring Security 的 FilterSecurityInterceptor 攔截器中:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
object.setAccessDecisionManager(customUrlDecisionManager);
object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
return object;
}
})
.and()
//...
}
大致上的邏輯就是如此,以上類完整程式碼小夥伴們可以參考 。
2.2 新方案
不過以上程式碼在目前最新的 Spring Security6 中用不了了,不是因為類過期了,而是因為類被移除了!哪個類被移除了?FilterSecurityInterceptor。
FilterSecurityInterceptor 這個過濾器以前是做許可權處理的,但是在新版的 Spring Security6 中,這個攔截器被 AuthorizationFilter 代替了。
老實說,新版的方案其實更合理一些,傳統的方案感覺帶有很多前後端不分的影子,現在就往更純粹的前後端分離奔去。
由於新版中連 FilterSecurityInterceptor 都不用了,所以舊版的方案顯然行不通了,新版的方案實際上更加簡單。
雖然新舊寫法不同,但是核心思路是一模一樣。
我們來看下新版的配置:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(register -> register.anyRequest().access((authentication, object) -> {
//表示請求的 URL 地址和資料庫的地址是否匹配上了
boolean isMatch = false;
//獲取當前請求的 URL 地址
String requestURI = object.getRequest().getRequestURI();
List<MenuWithRoleVO> menuWithRole = menuService.getMenuWithRole();
for (MenuWithRoleVO m : menuWithRole) {
if (antPathMatcher.match(m.getUrl(), requestURI)) {
isMatch = true;
//說明找到了請求的地址了
//這就是當前請求需要的角色
List<Role> roles = m.getRoles();
//獲取當前登入使用者的角色
Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
for (GrantedAuthority authority : authorities) {
for (Role role : roles) {
if (authority.getAuthority().equals(role.getName())) {
//說明當前登入使用者具備當前請求所需要的角色
return new AuthorizationDecision(true);
}
}
}
}
}
if (!isMatch) {
//說明請求的 URL 地址和資料庫的地址沒有匹配上,對於這種請求,統一隻要登入就能訪問
if (authentication.get() instanceof AnonymousAuthenticationToken) {
return new AuthorizationDecision(false);
} else {
//說明使用者已經認證了
return new AuthorizationDecision(true);
}
}
return new AuthorizationDecision(false);
}))
.formLogin(form ->
//...
)
.csrf(csrf ->
//...
)
.exceptionHandling(e ->
//...
)
.logout(logout ->
//...
);
return http.build();
}
核心思路還是和之前一樣,只不過現在的工作都在 access 方法中完成。
access 方法的回撥中有兩個引數,第一個引數是 authentication,很明顯,這就是當前登入成功的使用者物件,從這裡我們就可以提取出來當前使用者所具備的許可權。
第二個引數 object 實際上是一個 RequestAuthorizationContext,從這個裡邊可以提取出來當前請求物件 HttpServletRequest,進而提取出來當前請求的 URL 地址,然後依據許可權表中的資訊,判斷出當前請求需要什麼許可權,再和 authentication 中提取出來的當前使用者所具備的許可權進行對比即可。
如果當前登入使用者具備請求所需要的許可權,則返回 new AuthorizationDecision(true);
,否則返回 new AuthorizationDecision(false);
即可。
其實無論什麼框架,只要能把其中一個版本掌握個 70%,以後無論它怎麼升級,你都能快速上手!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2995586/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 基於Spring Security實現許可權管理系統Spring
- 使用動態路由實現許可權管理路由
- Spring Security實現基於RBAC的許可權表示式動態訪問控制Spring
- Security 10:許可權管理
- spring aop實現許可權管理Spring
- spring security許可權認證Spring
- Spring Security實現統一登入與許可權控制Spring
- Vue管理系統前端系列六動態路由-許可權管理實現Vue前端路由
- 提問:使用spring aop實現許可權管理Spring
- Spring security(五)-完美許可權管理系統(授權過程分析)Spring
- Spring Boot動態許可權變更實現的整體方案Spring Boot
- SpringBoot整合Spring security JWT實現介面許可權認證Spring BootJWT
- springBoot整合spring security實現許可權管理(單體應用版)--築基初期Spring Boot
- Spring Security 許可權管理的投票器與表決機制Spring
- 如何用 Vue 實現前端許可權控制(路由許可權 + 檢視許可權 + 請求許可權)Vue前端路由
- vue+element-ui實現動態的許可權管理和選單渲染VueUI
- 基於RBAC實現許可權管理
- Android6.0動態許可權管理庫Android
- jQuery實現的管理員許可權左右移動效果jQuery
- Spring Security 基於URL的許可權判斷Spring
- 關於動態許可權
- android動態許可權到自定義許可權框架Android框架
- Vue | 自定義指令和動態路由實現許可權控制Vue路由
- Spring Security原始碼分析十三:Spring Security 基於表示式的許可權控制Spring原始碼
- SpringBoot整合SpringSecurityOauth2實現鑑權-動態許可權Spring BootGseOAuth
- Spring Security + jwt 許可權系統設計,包含SQLSpringJWTSQL
- 需要一個前臺許可權管理,或問如何實現
- MySQL許可權管理實戰MySql
- 專欄丨Spring Security系列教程之Spring Security的四種許可權控制方式Spring
- Security8:許可權模擬
- NODE + JWT + Mongo(簡單實現許可權管理)JWTGo
- Java實現許可權管理-專案設計Java
- spring aop實現許可權控制,路徑控制Spring
- Linux-許可權管理(ACL許可權)Linux
- Android動態許可權總結Android
- 動態SQL 無許可權錯誤SQL
- delphi安卓動態許可權申請安卓
- Laravel實現許可權控制Laravel