Spring Security角色继承分析
今天想和小伙伴们来聊一聊SpringSecurity中的角色继承问题。
角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了。
SpringSecurity中为开发者提供了相关的角色继承解决方案,但是这一解决方案在最近的SpringSecurity版本变迁中,使用方法有所变化。今天除了和小伙伴们分享角色继承外,也来顺便说说这种变化,避免小伙伴们踩坑,同时购买了我的书的小伙伴也需要留意,书是基于SpringBoot2.0.4这个版本写的,这个话题和最新版SpringBoot的还是有一点差别。
以前的写法
这里说的以前写法,就是指SpringBoot2.0.8(含)之前的写法,在之前的写法中,角色继承只需要开发者提供一个RoleHierarchy接口的实例即可,例如下面这样:
@Bean
RoleHierarchyroleHierarchy(){
RoleHierarchyImplroleHierarchy=newRoleHierarchyImpl();
Stringhierarchy="ROLE_dba>ROLE_adminROLE_admin>ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
returnroleHierarchy;
}
在这里我们提供了一个RoleHierarchy接口的实例,使用字符串来描述了角色之间的继承关系,ROLE_dba具备ROLE_admin的所有权限,而ROLE_admin则具备ROLE_user的所有权限,继承与继承之间用一个空格隔开。提供了这个Bean之后,以后所有具备ROLE_user角色才能访问的资源,ROLE_dba和ROLE_admin也都能访问,具备ROLE_amdin角色才能访问的资源,ROLE_dba也能访问。
现在的写法
但是上面这种写法仅限于SpringBoot2.0.8(含)之前的版本,在之后的版本中,这种写法则不被支持,新版的写法是下面这样:
@Bean
RoleHierarchyroleHierarchy(){
RoleHierarchyImplroleHierarchy=newRoleHierarchyImpl();
Stringhierarchy="ROLE_dba>ROLE_admin\nROLE_admin>ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
returnroleHierarchy;
}
变化主要就是分隔符,将原来用空格隔开的地方,现在用换行符了。这里表达式的含义依然和上面一样,不再赘述。
上面两种不同写法都是配置角色的继承关系,配置完成后,接下来指定角色和资源的对应关系即可,如下:
@Override
protectedvoidconfigure(HttpSecurityhttp)throwsException{
http.authorizeRequests().antMatchers("/admin/**")
.hasRole("admin")
.antMatchers("/db/**")
.hasRole("dba")
.antMatchers("/user/**")
.hasRole("user")
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
这个表示/db/**格式的路径需要具备dba角色才能访问,/admin/**格式的路径则需要具备admin角色才能访问,/user/**格式的路径,则需要具备user角色才能访问,此时提供相关接口,会发现,dba除了访问/db/**,也能访问/admin/**和/user/**,admin角色除了访问/admin/**,也能访问/user/**,user角色则只能访问/user/**。
源码分析
这样两种不同的写法,其实也对应了两种不同的解析策略,角色继承关系的解析在RoleHierarchyImpl类的buildRolesReachableInOneStepMap方法中,SpringBoot2.0.8(含)之前该方法的源码如下:
privatevoidbuildRolesReachableInOneStepMap(){
Patternpattern=Pattern.compile("(\\s*([^\\s>]+)\\s*>\\s*([^\\s>]+))");
MatcherroleHierarchyMatcher=pattern
.matcher(this.roleHierarchyStringRepresentation);
this.rolesReachableInOneStepMap=newHashMap>();
while(roleHierarchyMatcher.find()){
GrantedAuthorityhigherRole=newSimpleGrantedAuthority(
roleHierarchyMatcher.group(2));
GrantedAuthoritylowerRole=newSimpleGrantedAuthority(
roleHierarchyMatcher.group(3));
SetrolesReachableInOneStepSet;
if(!this.rolesReachableInOneStepMap.containsKey(higherRole)){
rolesReachableInOneStepSet=newHashSet<>();
this.rolesReachableInOneStepMap.put(higherRole,
rolesReachableInOneStepSet);
}
else{
rolesReachableInOneStepSet=this.rolesReachableInOneStepMap
.get(higherRole);
}
addReachableRoles(rolesReachableInOneStepSet,lowerRole);
logger.debug("buildRolesReachableInOneStepMap()-Fromrole"+higherRole
+"onecanreachrole"+lowerRole+"inonestep.");
}
}
从这段源码中我们可以看到,角色的继承关系是通过正则表达式进行解析,通过空格进行切分,然后构建相应的map出来。
SpringBoot2.1.0(含)之后该方法的源码如下:
privatevoidbuildRolesReachableInOneStepMap(){
this.rolesReachableInOneStepMap=newHashMap>();
try(BufferedReaderbufferedReader=newBufferedReader(
newStringReader(this.roleHierarchyStringRepresentation))){
for(StringreadLine;(readLine=bufferedReader.readLine())!=null;){
String[]roles=readLine.split(">");
for(inti=1;irolesReachableInOneStepSet;
if(!this.rolesReachableInOneStepMap.containsKey(higherRole)){
rolesReachableInOneStepSet=newHashSet();
this.rolesReachableInOneStepMap.put(higherRole,rolesReachableInOneStepSet);
}else{
rolesReachableInOneStepSet=this.rolesReachableInOneStepMap.get(higherRole);
}
addReachableRoles(rolesReachableInOneStepSet,lowerRole);
if(logger.isDebugEnabled()){
logger.debug("buildRolesReachableInOneStepMap()-Fromrole"+higherRole
+"onecanreachrole"+lowerRole+"inonestep.");
}
}
}
}catch(IOExceptione){
thrownewIllegalStateException(e);
}
}
从这里我们可以看到,这里并没有一上来就是用正则表达式,而是先将角色继承字符串转为一个BufferedReader,然后一行一行的读出来,再进行解析,最后再构建相应的map。从这里我们可以看出为什么前后版本对此有不同的写法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。