节奏快起来
是的,现在不光Java在提速,连Spring的速度现在也提起来了。新的东西越来越多。现在Spring Boot的版本生命周期缩短了到半年,每半年就发一个版本。而Spring Security的版本生命周期也有类似的改变。
Spring Security版本生命周期
作为Spring工程师而言,学习的节奏也不得不加快了。这是一个残酷的事实,这里简单提一下,好了,我们回归正题。
Spring Security动态权限
如果你的接口权限非常稳定,我推荐你使用注解方式;反之,使用动态权限控制。动态权限控制更加灵活和贴近现实,但是开发成本也高。
最常见的方式
之前Spring Security提供的动态权限控制门槛挺高的,需要实现一个FilterInvocationSecurityMetadataSource
接口。而且目前网上大部分的教程还是这样的,因此就不再赘述了。
基于SpEL方式
上面方法的成本太高了,因此在后续的版本中基于Spring表达式语言又提供了一种可以实现动态权限的方式。这种方式有一定的要求:
- 首先要有一个Spring Bean。
- 这个Spring Bean必须包含一个公开方法;返回值为布尔值;参数列表有两个参数,第一个参数是当前认证的信息
Authentication
,第二个参数是当前请求HttpServletRequest
。很好理解就是拿着当前请求去和当前认证信息中包含的角色进行访问控制判断。 - 然后按照
@bean名称.方法名(authentication,request)
的格式配置到HttpSecurity
对象中。
伪代码是这样的:
代码语言:javascript复制 @Bean
RoleChecker roleChecker() {
// 包含了一个符合SpEL要求的方法
// boolean check(Authentication authentication, HttpServletRequest request);
return new JdbcRoleChecker();
}
配置到HttpSecurity:
代码语言:javascript复制 httpSecurity.authorizeRequests()
.anyRequest()
.access("@roleChecker.check(authentication,request)");
在Spring Security 5.6之前这样做是最简单的,需要你深入了解Spring Security和SpEL才行。
AuthorizationManager
Spring Security 5.6 增加了一个新的授权管理器接口AuthorizationManager<T>
,它让动态权限的控制接口化了。它用来检查当前认证信息Authentication
是否可以访问特定对象T
。上面的RoleChecker
不就是AuthorizationManager<HttpServletRequest>
么?AuthorizationManager
将这种访问决策抽象的更加泛化。
@FunctionalInterface
public interface AuthorizationManager<T> {
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
// 授权决策没有经过允许就403
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
// todo 没有null 的情况
}
// 钩子方法。
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
在Spring Security 5.6中,我们就可以这样去实现了:
代码语言:javascript复制 httpSecurity.authorizeHttpRequests()
.anyRequest()
.access((authenticationSupplier, requestAuthorizationContext) -> {
// 当前用户的权限信息 比如角色
Collection<? extends GrantedAuthority> authorities = authenticationSupplier.get().getAuthorities();
// 当前请求上下文
// 我们可以获取携带的参数
Map<String, String> variables = requestAuthorizationContext.getVariables();
// 我们可以获取原始request对象
HttpServletRequest request = requestAuthorizationContext.getRequest();
//todo 根据这些信息 和业务写逻辑即可 最终决定是否授权 isGranted
boolean isGranted = true;
return new AuthorizationDecision(isGranted);
});
这样门槛是不是低多了呢?
你还会选择每次修改权限都要打jar包发版的注解权限控制吗?