spring security通过启用方法拦截,控制访问权限
注解@EnableGlobalMethodSecurity
可以开启spring security的全局安全设置,其自动化配置位于类GlobalMethodSecurityConfiguration
中。
该注解提供了三种类型的安全设置:
是否开启方法前后拦截,默认为false。
设置true
可开启@PreAuthorize
和@PostAuthorize
, 这两个注解可接受一个EL表达式,非常灵活的控制权限。从字面意思可直观看出,一个用于进入方法前,一个用于进入方法后,后者等同于放开权限,但是控制返回。
可开启@PreFilter
和@PostFilter
,从字面意思可以看出,用于方法前后的两个注解,该注解用于操作入参和出参。
是否开启@Secure
,默认false,该注解仅接受一个角色/权限,一般用于粗劣度控制。
使用较少,从字面意思可以看出属于jsr250
的范畴,目前位于jakarta
项目。
启用jsr250
,可以使用如 @DenyAll
@AllowAll
@RolesAllowed
等注解。
树形结构的组织架构是一种比较常见的架构形式,基于组织架构的角色授予也通常会表显出一种树形结构,节点之上的角色通常会拥有子节点的权限,也表现出一种树形结构。
比如:
此时文字表示即:Role_经理 > Role_组长 > Role_成员
在接口方法上使用@PreAuthorize
控制权限的写法即 @PreAuthorize("hasRole('Role_经理')")
[依此类推]
为了实现角色继承,需要使用接口RoleHierarchy
该接口只有一个方法,返回了角色的层级关系。
非常简单,不啰嗦,直接上代码。
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
Map<String, List<String>> map = MapBuilder.create(new HashMap<String, List<String>>())
.put("ROLE_经理", Lists.newArrayList("ROLE_组长", "ROLE_成员"))
.put("ROLE_组长", Lists.newArrayList("ROLE_成员"))
.build();
roleHierarchy.setHierarchy(RoleHierarchyUtils.roleHierarchyFromMap(map));
return roleHierarchy;
}
只需要提供一个该接口的实现并注册即可
RoleHierarchyImpl
是spring已经给我们的一种实现
为什么这么简单即可实现角色的继承控制,其实就是一个AOP的问题。
找到@PreAuthorize
是怎么实现功能的接口。
类ReactiveMethodSecurityConfiguration
中定义了一个方法拦截器
@Bean
public PrePostAdviceReactiveMethodInterceptor securityMethodInterceptor(AbstractMethodSecurityMetadataSource source, MethodSecurityExpressionHandler handler) {
ExpressionBasedPostInvocationAdvice postAdvice = new ExpressionBasedPostInvocationAdvice(
handler);
ExpressionBasedPreInvocationAdvice preAdvice = new ExpressionBasedPreInvocationAdvice();
preAdvice.setExpressionHandler(handler);
return new PrePostAdviceReactiveMethodInterceptor(source, preAdvice, postAdvice);
}
这里头的条条框框不细说,吧唧吧唧很多,最后就是拦截@PreAuthorize
这个注解,然后解析这个注解里面的表达式。
跟代码最后会进入到MethodSecurityExpressionRoot这个里头,但是这个是依赖其父类SecurityExpressionRoot来实现的,这个父类提供的一系列final方法,都可以用到@PreAuthorize注解
中(既然是EL表达式,当然支持逻辑运算与或非),找到hasRole方法开始跟代码(也许聪明如你,通过idea的插件,直接找到了这里,.)
private Set<String> getAuthoritySet() {
if (roles == null) {
Collection<? extends GrantedAuthority> userAuthorities = authentication
.getAuthorities();
if (roleHierarchy != null) {
userAuthorities = roleHierarchy
.getReachableGrantedAuthorities(userAuthorities);
}
roles = AuthorityUtils.authorityListToSet(userAuthorities);
}
return roles;
}
这里获取用户的角色时(字面意思看起来叫权限,角色=权限的集合),判断了如果注入了角色继承就会计算一把角色继承关系。
再顺着这里反过来找,最后会找到前面说的GlobalMethodSecurityConfiguration
中
在方法afterSingletonsInstantiated
中有一段:
RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
if (roleHierarchy != null) {
this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
}
此处获取了RoleHierarchy
的一个单例实例,并注入进来。