Spring Security动态权限控制居然如此简单 | Java Debug 笔记
本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接
之前在动态权限控制的教程中,我们通过自定义 FilterInvocationSecurityMetadataSource
和 AccessDecisionManager
两个接口实现了动态权限控制。这里需要我们做的事情比较多,有一定的学习成本。今天来介绍一种更加简单和容易理解的方法实现动态权限控制。
基于表达式的访问控制
httpSecurity.authorizeRequests()
.anyRequest()
.access("hasRole('admin')")
复制代码
这种方式不用多说了吧,我们配置了表达式 hasRole('admin')
后,Spring Security会调用 SecurityExpressionRoot
的 hasRole(String role)
方法来判断当前用户是否持有角色 admin
,进而作出是否放行的决策。这种方式除了可以静态的权限控制之外还能够动态的权限控制。
基于Bean的访问控制表达式
Spring Security扩展了对表达式进行了扩展,支持引用任何公开的 Spring Bean,假如我们有一个实现下列接口的 Spring Bean:
/**
* 角色检查器接口.
*
* @author n1
* @since 2021 /4/6 16:28
*/
public interface RoleChecker extends InitializingBean {
/**
* Check boolean.
*
* @param authentication the authentication
* @param request the request
* @return the boolean
*/
boolean check(Authentication authentication, HttpServletRequest request);
}
复制代码
基于JDBC的角色检查,最好这里做个缓存:
/**
* 基于jdbc的角色检查 最好这里做个缓存
* @author n1
* @since 2021/4/6 16:43
*/
public class JdbcRoleChecker implements RoleChecker {
// 系统集合的抽象实现,这里你可以采用更加合理更加效率的方式
private Supplier> supplier;
@Override
public boolean check(Authentication authentication, HttpServletRequest request) {
Collection authorities = authentication.getAuthorities();
// 当前用户的角色集合
System.out.println("authorities = " + authorities);
//todo 这里自行实现比对逻辑
// supplier.get().stream().filter(matcher -> matcher.matches(request));
// true false 为是否放行
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(supplier.get(), "function must not be null");
}
}
复制代码
我们就可以这样配置 HttpSecurity
:
httpSecurity.authorizeRequests()
.anyRequest()
.access("@roleChecker.check(authentication,request)")
复制代码
通过 RoleChecker
中的 Authentication
我们可以获得当前用户的信息,尤其是权限集。通过 HttpServletRequest
我们可以获得当前请求的 URI。该 URI在系统中的权限集和用户的权限集进行交集判断就能作出正确的访问决策。
路径参数
有些时候我们的访问 URI中还包含了路径参数,例如 /foo/{id}
。我们也可以通过基于Bean的访问控制表达式结合具体的 id
值来控制。这时应该这么写:
/**
* 角色检查器接口.
*
* @author n1
* @since 2021 /4/6 16:28
*/
public interface RoleChecker extends InitializingBean {
/**
* Check boolean.
*
* @param authentication the authentication
* @param request the request
* @return the boolean
*/
boolean check(Authentication authentication, String id);
}
复制代码
对应的配置为:
httpSecurity.authorizeRequests()
.antMatchers("/foo/{id}/**")
.access("@roleChecker.check(authentication,#id)")
复制代码
这样当 /foo/123
请求被拦截后, 123
就会赋值给 check
方法中的 id
处理。
总结
这种表达式的动态权限控制比之前的方式更加容易掌握和理解。但是它也有它的局限性,比如表达式中的方法中的参数类型比较单一。而通过 FilterInvocationSecurityMetadataSource
的方式则更加强大可以自定义一些访问决策,适合更加复杂的场景。我是: 码农小胖哥,多多关注,分享更多原创编程干货。