日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Spring Security 中如何讓上級擁有下級的所有權限(案例分析)

瀏覽:37日期:2023-08-11 14:43:03

答案是能!

松哥之前寫過類似的文章,但是主要是講了用法,今天我們來看看原理!

本文基于當前 Spring Security 5.3.4 來分析,為什么要強調最新版呢?因為在在 5.0.11 版中,角色繼承配置和現在不一樣。舊版的方案我們現在不討論了,直接來看當前最新版是怎么處理的。

1.角色繼承案例

我們先來一個簡單的權限案例。

創建一個 Spring Boot 項目,添加 Spring Security 依賴,并創建兩個測試用戶,如下:

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser('javaboy') .password('{noop}123').roles('admin') .and() .withUser('江南一點雨') .password('{noop}123') .roles('user');}

然后準備三個測試接口,如下:

@RestControllerpublic class HelloController { @GetMapping('/hello') public String hello() { return 'hello'; } @GetMapping('/admin/hello') public String admin() { return 'admin'; } @GetMapping('/user/hello') public String user() { return 'user'; }}

這三個測試接口,我們的規劃是這樣的:

/hello 是任何人都可以訪問的接口 /admin/hello 是具有 admin 身份的人才能訪問的接口 /user/hello 是具有 user 身份的人才能訪問的接口 所有 user 能夠訪問的資源,admin 都能夠訪問

注意第四條規范意味著所有具備 admin 身份的人自動具備 user 身份。

接下來我們來配置權限的攔截規則,在 Spring Security 的 configure(HttpSecurity http) 方法中,代碼如下:

http.authorizeRequests() .antMatchers('/admin/**').hasRole('admin') .antMatchers('/user/**').hasRole('user') .anyRequest().authenticated() .and() ... ...

這里的匹配規則我們采用了 Ant 風格的路徑匹配符,Ant 風格的路徑匹配符在 Spring 家族中使用非常廣泛,它的匹配規則也非常簡單:

通配符 含義 ** 匹配多層路徑 * 匹配一層路徑 ? 匹配任意單個字符

上面配置的含義是:

如果請求路徑滿足 /admin/** 格式,則用戶需要具備 admin 角色。 如果請求路徑滿足 /user/** 格式,則用戶需要具備 user 角色。 剩余的其他格式的請求路徑,只需要認證(登錄)后就可以訪問。

注意代碼中配置的三條規則的順序非常重要,和 Shiro 類似,Spring Security 在匹配的時候也是按照從上往下的順序來匹配,一旦匹配到了就不繼續匹配了,所以攔截規則的順序不能寫錯

如果使用角色繼承,這個功能很好實現,我們只需要在 SecurityConfig 中添加如下代碼來配置角色繼承關系即可:

@BeanRoleHierarchy roleHierarchy() { RoleHierarchyImpl hierarchy = new RoleHierarchyImpl(); hierarchy.setHierarchy('ROLE_admin > ROLE_user'); return hierarchy;}

注意,在配置時,需要給角色手動加上 ROLE_ 前綴。上面的配置表示 ROLE_admin 自動具備 ROLE_user 的權限。

接下來,我們啟動項目進行測試。

項目啟動成功后,我們首先以 江南一點雨的身份進行登錄:

Spring Security 中如何讓上級擁有下級的所有權限(案例分析)

登錄成功后,分別訪問 /hello,/admin/hello 以及 /user/hello 三個接口,其中:

/hello 因為登錄后就可以訪問,這個接口訪問成功。 /admin/hello 需要 admin 身份,所以訪問失敗。 /user/hello 需要 user 身份,所以訪問成功。

再以 javaboy 身份登錄,登錄成功后,我們發現 javaboy 也能訪問 /user/hello 這個接口了,說明我們的角色繼承配置沒問題!

2.原理分析

這里配置的核心在于我們提供了一個 RoleHierarchy 實例,所以我們的分析就從該類入手。

RoleHierarchy 是一個接口,該接口中只有一個方法:

public interface RoleHierarchy {Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(Collection<? extends GrantedAuthority> authorities);}

這個方法參數 authorities 是一個權限集合,從方法名上看方法的返回值是一個可訪問的權限集合。

舉個簡單的例子,假設角色層次結構是 ROLE_A > ROLE_B > ROLE_C,現在直接給用戶分配的權限是 ROLE_A,但實際上用戶擁有的權限有 ROLE_A、ROLE_B 以及 ROLE_C。

getReachableGrantedAuthorities 方法的目的就是是根據角色層次定義,將用戶真正可以觸達的角色解析出來。

RoleHierarchy 接口有兩個實現類,如下圖:

Spring Security 中如何讓上級擁有下級的所有權限(案例分析)

NullRoleHierarchy 這是一個空的實現,將傳入的參數原封不動返回。 RoleHierarchyImpl 這是我們上文所使用的實現,這個會完成一些解析操作。

我們來重點看下 RoleHierarchyImpl 類。

這個類中實際上就四個方法 setHierarchy、getReachableGrantedAuthorities、buildRolesReachableInOneStepMap 以及 buildRolesReachableInOneOrMoreStepsMap,我們來逐個進行分析。

首先是我們一開始調用的 setHierarchy 方法,這個方法用來設置角色層級關系:

public void setHierarchy(String roleHierarchyStringRepresentation) {this.roleHierarchyStringRepresentation = roleHierarchyStringRepresentation;if (logger.isDebugEnabled()) {logger.debug('setHierarchy() - The following role hierarchy was set: '+ roleHierarchyStringRepresentation);}buildRolesReachableInOneStepMap();buildRolesReachableInOneOrMoreStepsMap();}

用戶傳入的字符串變量設置給 roleHierarchyStringRepresentation 屬性,然后通過 buildRolesReachableInOneStepMap 和 buildRolesReachableInOneOrMoreStepsMap 方法完成對角色層級的解析。

buildRolesReachableInOneStepMap 方法用來將角色關系解析成一層一層的形式。我們來看下它的源碼:

private void buildRolesReachableInOneStepMap() {this.rolesReachableInOneStepMap = new HashMap<>();for (String line : this.roleHierarchyStringRepresentation.split('n')) {String[] roles = line.trim().split('s+>s+');for (int i = 1; i < roles.length; i++) {String higherRole = roles[i - 1];GrantedAuthority lowerRole = new SimpleGrantedAuthority(roles[i]);Set<GrantedAuthority> rolesReachableInOneStepSet;if (!this.rolesReachableInOneStepMap.containsKey(higherRole)) {rolesReachableInOneStepSet = new HashSet<>();this.rolesReachableInOneStepMap.put(higherRole, rolesReachableInOneStepSet);} else {rolesReachableInOneStepSet = this.rolesReachableInOneStepMap.get(higherRole);}rolesReachableInOneStepSet.add(lowerRole);}}}

首先大家看到,按照換行符來解析用戶配置的多個角色層級,這是什么意思呢?

我們前面案例中只是配置了 ROLE_admin > ROLE_user,如果你需要配置多個繼承關系,怎么配置呢?多個繼承關系用 n 隔開即可,如下 ROLE_A > ROLE_B n ROLE_C > ROLE_D。還有一種情況,如果角色層級關系是連續的,也可以這樣配置 ROLE_A > ROLE_B > ROLE_C > ROLE_D。

所以這里先用 n 將多層繼承關系拆分開形成一個數組,然后對數組進行遍歷。

在具體遍歷中,通過 > 將角色關系拆分成一個數組,然后對數組進行解析,高一級的角色作為 key,低一級的角色作為 value。

代碼比較簡單,最終的解析出來存入 rolesReachableInOneStepMap 中的層級關系是這樣的:

假設角色繼承關系是 ROLE_A > ROLE_B n ROLE_C > ROLE_D n ROLE_C > ROLE_E,Map 中的數據是這樣:

A?>B C?>[D,E]

假設角色繼承關系是 ROLE_A > ROLE_B > ROLE_C > ROLE_D,Map 中的數據是這樣:

A?>B B?>C C?>D

這是 buildRolesReachableInOneStepMap 方法解析出來的 rolesReachableInOneStepMap 集合。

接下來的 buildRolesReachableInOneOrMoreStepsMap 方法則是對 rolesReachableInOneStepMap 集合進行再次解析,將角色的繼承關系拉平。

例如 rolesReachableInOneStepMap 中保存的角色繼承關系如下:

A?>B B?>C C?>D

經過 buildRolesReachableInOneOrMoreStepsMap 方法解析之后,新的 Map 中保存的數據如下:

A?>[B、C、D] B?>[C、D] C?>D

這樣解析完成后,每一個角色可以觸達到的角色就一目了然了。

我們來看下 buildRolesReachableInOneOrMoreStepsMap 方法的實現邏輯:

private void buildRolesReachableInOneOrMoreStepsMap() {this.rolesReachableInOneOrMoreStepsMap = new HashMap<>();for (String roleName : this.rolesReachableInOneStepMap.keySet()) {Set<GrantedAuthority> rolesToVisitSet = new HashSet<>(this.rolesReachableInOneStepMap.get(roleName));Set<GrantedAuthority> visitedRolesSet = new HashSet<>();while (!rolesToVisitSet.isEmpty()) {GrantedAuthority lowerRole = rolesToVisitSet.iterator().next();rolesToVisitSet.remove(lowerRole);if (!visitedRolesSet.add(lowerRole) ||!this.rolesReachableInOneStepMap.containsKey(lowerRole.getAuthority())) {continue;} else if (roleName.equals(lowerRole.getAuthority())) {throw new CycleInRoleHierarchyException();}rolesToVisitSet.addAll(this.rolesReachableInOneStepMap.get(lowerRole.getAuthority()));}this.rolesReachableInOneOrMoreStepsMap.put(roleName, visitedRolesSet);}}

這個方法還比較巧妙。首先根據 roleName 從 rolesReachableInOneStepMap 中獲取對應的 rolesToVisitSet,這個 rolesToVisitSet 是一個 Set 集合,對其進行遍歷,將遍歷結果添加到 visitedRolesSet 集合中,如果 rolesReachableInOneStepMap 集合的 key 不包含當前讀取出來的 lowerRole,說明這個 lowerRole 就是整個角色體系中的最底層,直接 continue。否則就把 lowerRole 在 rolesReachableInOneStepMap 中對應的 value 拿出來繼續遍歷。

最后將遍歷結果存入 rolesReachableInOneOrMoreStepsMap 集合中即可。

這個方法有點繞,小伙伴們可以自己打個斷點品一下。

看了上面的分析,小伙伴們可能發現了,其實角色繼承,最終還是拉平了去對比。

我們定義的角色有層級,但是代碼中又將這種層級拉平了,方便后續的比對。

最后還有一個 getReachableGrantedAuthorities 方法,根據傳入的角色分析出其可能潛在包含的一些角色:

public Collection<GrantedAuthority> getReachableGrantedAuthorities(Collection<? extends GrantedAuthority> authorities) {if (authorities == null || authorities.isEmpty()) {return AuthorityUtils.NO_AUTHORITIES;}Set<GrantedAuthority> reachableRoles = new HashSet<>();Set<String> processedNames = new HashSet<>();for (GrantedAuthority authority : authorities) {if (authority.getAuthority() == null) {reachableRoles.add(authority);continue;}if (!processedNames.add(authority.getAuthority())) {continue;}reachableRoles.add(authority);Set<GrantedAuthority> lowerRoles = this.rolesReachableInOneOrMoreStepsMap.get(authority.getAuthority());if (lowerRoles == null) {continue;}for (GrantedAuthority role : lowerRoles) {if (processedNames.add(role.getAuthority())) {reachableRoles.add(role);}}}List<GrantedAuthority> reachableRoleList = new ArrayList<>(reachableRoles.size());reachableRoleList.addAll(reachableRoles);return reachableRoleList;}

這個方法的邏輯比較直白,就是從 rolesReachableInOneOrMoreStepsMap 集合中查詢出當前角色真正可訪問的角色信息。

3.RoleHierarchyVoter

getReachableGrantedAuthorities 方法將在 RoleHierarchyVoter 投票器中被調用。

public class RoleHierarchyVoter extends RoleVoter {private RoleHierarchy roleHierarchy = null;public RoleHierarchyVoter(RoleHierarchy roleHierarchy) {Assert.notNull(roleHierarchy, 'RoleHierarchy must not be null');this.roleHierarchy = roleHierarchy;}@OverrideCollection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {return roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());}}

關于 Spring Security 投票器,將是另外一個故事,松哥將在下篇文章中和小伙伴們分享投票器和決策器~

4.小結

到此這篇關于Spring Security 中如何讓上級擁有下級的所有權限的文章就介紹到這了,更多相關Spring Security上級擁有下級的所有權限內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
综合激情网...| 好看的亚洲午夜视频在线| 国产综合精品一区| 国产 日韩 欧美一区| 伊人久久高清| 亚洲91久久| 麻豆精品一区二区综合av| 视频一区二区中文字幕| 蜜桃av一区二区三区电影| 国产麻豆一区| 国产精品theporn| 久久av国产紧身裤| 国产精品视频一区视频二区| 天堂va欧美ⅴa亚洲va一国产| 欧美日韩亚洲三区| 日韩网站中文字幕| 欧美日韩在线网站| 免费日韩av片| 国产激情一区| 午夜宅男久久久| 久久久久九九精品影院| 亚洲特色特黄| 视频一区国产视频| 精品久久一区| 亚洲先锋成人| 国产成人精品一区二区三区免费 | 国产专区精品| 麻豆成人91精品二区三区| 久久国产影院| 黑丝一区二区三区| 亚洲三级观看| 精品视频在线观看网站| 国产日韩电影| 国产精品99视频| 亚洲福利精品| 国产精品久久久久毛片大屁完整版 | 鲁大师精品99久久久| 六月婷婷综合| 美腿丝袜亚洲三区| 日韩专区欧美专区| 精品伊人久久久| 国产国产精品| 成人久久一区| 久久久亚洲欧洲日产| 天堂成人免费av电影一区| 国产日韩视频在线| 视频一区中文字幕精品 | 91成人在线精品视频| 久久久久亚洲精品中文字幕| 亚洲我射av| 911亚洲精品| 日韩不卡免费高清视频| 欧美 日韩 国产一区二区在线视频 | 精品欠久久久中文字幕加勒比| 91日韩在线| 午夜在线播放视频欧美| 国产人成精品一区二区三| 日韩久久电影| 亚洲精品字幕| 欧美日本不卡| 综合日韩在线| 国产欧美91| 亚洲午夜天堂| 亚洲成人精品| 国产美女撒尿一区二区| 欧美xxxx性| 国产精品久久| 香蕉国产精品| 亚洲精品激情| 国产h片在线观看| 亚洲精品女人| 日韩影院二区| 亚洲精品无播放器在线播放| 国产v日韩v欧美v| 综合激情一区| 久久男女视频| 麻豆91精品视频| 亚洲精选成人| 91精品国产自产在线观看永久∴ | 国产精品久久久久蜜臀| 另类亚洲自拍| 日韩中文欧美| 国产精品久久久久久妇女| 亚洲欧美日韩国产综合精品二区| 国产成人精品一区二区三区免费 | 精品中文在线| 午夜亚洲福利| 国产午夜精品一区二区三区欧美 | 国产精品免费精品自在线观看| 婷婷综合社区| 日韩欧美一区二区三区免费看| 欧美午夜三级| 悠悠资源网久久精品| www在线观看黄色| 1024精品一区二区三区| 欧美伊人影院| 久久久成人网| 亚洲最大av| 日韩成人亚洲| 欧美日韩中出| 综合亚洲视频| 久久久久久久久99精品大| 国产日韩一区二区三区在线播放| 日产精品一区| 麻豆国产欧美日韩综合精品二区| 国产一区二区精品| 久久天堂影院| 亚洲精品在线影院| 群体交乱之放荡娇妻一区二区| 免播放器亚洲一区| 私拍精品福利视频在线一区| 影音先锋国产精品| 日韩国产专区| 国产激情综合| 亚洲精品亚洲人成在线观看| 噜噜噜躁狠狠躁狠狠精品视频 | 欧美福利专区| 不卡在线一区| 婷婷中文字幕一区| 香蕉成人av| 亚洲区第一页| 日本成人在线网站| 久久国产精品色av免费看| 日本中文字幕一区二区| 欧美成人亚洲| 亚洲精品麻豆| 日韩免费av| 国产精品久久久久久久免费软件| 狠狠干综合网| 中文国产一区| 成人久久一区| 伊人久久大香线蕉av不卡| 国产精品15p| 日本不卡一区二区三区| 日韩在线观看中文字幕| 亚洲男女自偷自拍| 蜜桃久久av一区| 性色一区二区| 久久亚洲色图| 黄色亚洲在线| 综合色就爱涩涩涩综合婷婷| 婷婷国产精品| 国产综合亚洲精品一区二| 在线一区视频观看| 国产亚洲观看| 国产精品久久久久久久久久久久久久久 | 免费人成精品欧美精品| 在线看片日韩| 久久精品青草| 美女久久久久| 一区在线视频观看| 伊人影院久久| 亚洲精品婷婷| 亚洲人亚洲人色久| 国产欧美二区| 久久女人天堂| 精品美女在线视频| 欧洲av不卡| 日韩一区欧美二区| 日韩精品免费视频人成 | 亚洲精品观看| 国产精品a级| 国产一区二区三区四区二区| 日本欧美不卡| 香蕉成人av| 精品久久亚洲| 成人福利视频| 日韩欧美久久| 成人午夜毛片| 亚洲精品日本| 9色国产精品| 国产乱码午夜在线视频| 麻豆91精品| 亚洲女同av| 麻豆精品国产91久久久久久| 久久最新视频| 精品三级久久| 久久久免费人体| 天堂va在线高清一区| 尤物在线精品| 久久国产日本精品| 日韩精品欧美大片| 精品视频一区二区三区四区五区| 免费人成在线不卡| 四虎国产精品免费观看| 久久精品青草| 久久99影视| 国产精品一站二站| 国产精品原创| 亚洲我射av| 麻豆成人av在线| 99在线精品免费视频九九视 | 国产伦精品一区二区三区视频| 桃色一区二区| 在线观看亚洲精品福利片| 久久精品一区二区三区中文字幕| 91精品二区| 亚洲国产专区| 亚洲欧洲一区| 91亚洲一区|