Spring Security角色繼承分析
今天想和小伙伴們來(lái)聊一聊 Spring Security 中的角色繼承問(wèn)題。
角色繼承實(shí)際上是一個(gè)很常見的需求,因?yàn)榇蟛糠止局卫砜赡芏际墙鹱炙蔚模纤究赡芫邆湎聦俚牟糠稚踔了袡?quán)限,這一現(xiàn)實(shí)場(chǎng)景,反映到我們的代碼中,就是角色繼承了。
Spring Security 中為開發(fā)者提供了相關(guān)的角色繼承解決方案,但是這一解決方案在最近的 Spring Security 版本變遷中,使用方法有所變化。今天除了和小伙伴們分享角色繼承外,也來(lái)順便說(shuō)說(shuō)這種變化,避免小伙伴們踩坑,同時(shí)購(gòu)買了我的書的小伙伴也需要留意,書是基于 Spring Boot2.0.4 這個(gè)版本寫的,這個(gè)話題和最新版 Spring Boot 的還是有一點(diǎn)差別。
以前的寫法
這里說(shuō)的以前寫法,就是指 SpringBoot2.0.8(含)之前的寫法,在之前的寫法中,角色繼承只需要開發(fā)者提供一個(gè) RoleHierarchy 接口的實(shí)例即可,例如下面這樣:
@Bean RoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_dba > ROLE_admin ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy; }
在這里我們提供了一個(gè) RoleHierarchy 接口的實(shí)例,使用字符串來(lái)描述了角色之間的繼承關(guān)系, ROLE_dba 具備 ROLE_admin 的所有權(quán)限,而 ROLE_admin 則具備 ROLE_user 的所有權(quán)限,繼承與繼承之間用一個(gè)空格隔開。提供了這個(gè) Bean 之后,以后所有具備 ROLE_user 角色才能訪問(wèn)的資源, ROLE_dba 和 ROLE_admin 也都能訪問(wèn),具備 ROLE_amdin 角色才能訪問(wèn)的資源, ROLE_dba 也能訪問(wèn)。
現(xiàn)在的寫法
但是上面這種寫法僅限于 Spring Boot2.0.8(含)之前的版本,在之后的版本中,這種寫法則不被支持,新版的寫法是下面這樣:
@Bean RoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy; }
變化主要就是分隔符,將原來(lái)用空格隔開的地方,現(xiàn)在用換行符了。這里表達(dá)式的含義依然和上面一樣,不再贅述。
上面兩種不同寫法都是配置角色的繼承關(guān)系,配置完成后,接下來(lái)指定角色和資源的對(duì)應(yīng)關(guān)系即可,如下:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/admin/**") .hasRole("admin") .antMatchers("/db/**") .hasRole("dba") .antMatchers("/user/**") .hasRole("user") .and() .formLogin() .loginProcessingUrl("/doLogin") .permitAll() .and() .csrf().disable(); }
這個(gè)表示 /db/** 格式的路徑需要具備 dba 角色才能訪問(wèn), /admin/** 格式的路徑則需要具備 admin 角色才能訪問(wèn), /user/** 格式的路徑,則需要具備 user 角色才能訪問(wèn),此時(shí)提供相關(guān)接口,會(huì)發(fā)現(xiàn),dba 除了訪問(wèn) /db/** ,也能訪問(wèn) /admin/** 和 /user/** ,admin 角色除了訪問(wèn) /admin/** ,也能訪問(wèn) /user/** ,user 角色則只能訪問(wèn) /user/** 。
源碼分析
這樣兩種不同的寫法,其實(shí)也對(duì)應(yīng)了兩種不同的解析策略,角色繼承關(guān)系的解析在 RoleHierarchyImpl 類的 buildRolesReachableInOneStepMap 方法中,Spring Boot2.0.8(含)之前該方法的源碼如下:
private void buildRolesReachableInOneStepMap() { Pattern pattern = Pattern.compile("(\\s*([^\\s>]+)\\s*>\\s*([^\\s>]+))"); Matcher roleHierarchyMatcher = pattern .matcher(this.roleHierarchyStringRepresentation); this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>(); while (roleHierarchyMatcher.find()) { GrantedAuthority higherRole = new SimpleGrantedAuthority( roleHierarchyMatcher.group(2)); GrantedAuthority lowerRole = new SimpleGrantedAuthority( roleHierarchyMatcher.group(3)); Set<GrantedAuthority> rolesReachableInOneStepSet; if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) { rolesReachableInOneStepSet = new HashSet<>(); this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); } else { rolesReachableInOneStepSet = this.rolesReachableInOneStepMap .get(higherRole); } addReachableRoles(rolesReachableInOneStepSet, lowerRole); logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole + " one can reach role " + lowerRole + " in one step."); } }
從這段源碼中我們可以看到,角色的繼承關(guān)系是通過(guò)正則表達(dá)式進(jìn)行解析,通過(guò)空格進(jìn)行切分,然后構(gòu)建相應(yīng)的 map 出來(lái)。
Spring Boot2.1.0(含)之后該方法的源碼如下:
private void buildRolesReachableInOneStepMap() { this.rolesReachableInOneStepMap = new HashMap<GrantedAuthority, Set<GrantedAuthority>>(); try (BufferedReader bufferedReader = new BufferedReader( new StringReader(this.roleHierarchyStringRepresentation))) { for (String readLine; (readLine = bufferedReader.readLine()) != null;) { String[] roles = readLine.split(" > "); for (int i = 1; i < roles.length; i++) { GrantedAuthority higherRole = new SimpleGrantedAuthority( roles[i - 1].replaceAll("^\\s+|\\s+$", "")); GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i].replaceAll("^\\s+|\\s+$ Set<GrantedAuthority> rolesReachableInOneStepSet; if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) { rolesReachableInOneStepSet = new HashSet<GrantedAuthority>(); this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet); } else { rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole); } addReachableRoles(rolesReachableInOneStepSet, lowerRole); if (logger.isDebugEnabled()) { logger.debug("buildRolesReachableInOneStepMap() - From role " + higherRole + " one can reach role " + lowerRole + " in one step."); } } } } catch (IOException e) { throw new IllegalStateException(e); } }
從這里我們可以看到,這里并沒有一上來(lái)就是用正則表達(dá)式,而是先將角色繼承字符串轉(zhuǎn)為一個(gè) BufferedReader ,然后一行一行的讀出來(lái),再進(jìn)行解析,最后再構(gòu)建相應(yīng)的 map。從這里我們可以看出為什么前后版本對(duì)此有不同的寫法。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Spring security實(shí)現(xiàn)登陸和權(quán)限角色控制
- Spring Security OAuth2認(rèn)證授權(quán)示例詳解
- 基于Spring Security的Oauth2授權(quán)實(shí)現(xiàn)方法
- Vue 動(dòng)態(tài)路由的實(shí)現(xiàn)及 Springsecurity 按鈕級(jí)別的權(quán)限控制
- 淺談Spring Security 對(duì)于靜態(tài)資源的攔截與放行
- SpringBoot2.0 整合 SpringSecurity 框架實(shí)現(xiàn)用戶權(quán)限安全管理方法
- 使用Spring Security OAuth2實(shí)現(xiàn)單點(diǎn)登錄
- 自定義Spring Security的身份驗(yàn)證失敗處理方法
相關(guān)文章
MyBatis通用Mapper和PageHelper的過(guò)程詳解
這篇文章主要介紹了MyBatis通用Mapper和PageHelper的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11詳解使用Spring Cloud Consul實(shí)現(xiàn)服務(wù)的注冊(cè)和發(fā)現(xiàn)
這篇文章主要介紹了詳解使用Spring Cloud Consul實(shí)現(xiàn)服務(wù)的注冊(cè)和發(fā)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Spring中BeanUtils.copyProperties的坑及解決
這篇文章主要介紹了Spring中BeanUtils.copyProperties的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java應(yīng)用注冊(cè)成Windows服務(wù)實(shí)現(xiàn)自啟的教程詳解
這篇文章主要給大家介紹了Java應(yīng)用注冊(cè)成Windows服務(wù)實(shí)現(xiàn)自啟的教程,文中有詳細(xì)的代碼示例和圖文講解供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2024-02-02springboot實(shí)現(xiàn)瀏覽器截屏并添加文字
大家好,本篇文章主要講的是springboot實(shí)現(xiàn)瀏覽器截屏并添加文字,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-02-02