Spring Security之动态配置资源权限
在Spring Security中实现通过数据库动态配置url资源权限,需要通过配置验证过滤器来实现资源权限的加载、验证。系统启动时,到数据库加载系统资源权限列表,当有请求访问时,通过对比系统资源权限列表和用户资源权限列表(在用户登录时添加到用户信息中)来判断用户是否有该url的访问权限。
在配置验证过滤器时需要的配置项有如下几个:
- filterSecurityInterceptor:通过继承AbstractSecurityInterceptor并实现Filter接口自定义一个验证过滤器,替换默认验证过滤器。
- accessDecisionManager:通过实现AccessDecisionManager接口自定义一个决策管理器,判断是否有访问权限。判断逻辑可以写在决策管理器的决策方法中,也可以通过投票器实现,除了框架提供的三种投票器还可以添加自定义投票器。自定义投票器通过实现AccessDecisionVoter接口来实现。
- securityMetadataSource:实现FilterInvocationSecurityMetadataSource接口,在实现类中加载资源权限,并在filterSecurityInterceptor中注入该实现类。
- WebSecurityConfig:系统配置类,需要在配置类中配置启用filterSecurityInterceptor。
securityMetadataSource这里简单理解为资源权限数据源,主要维护系统的资源权限信息。系统启动时,可以将权限资源信息从配置文件、数据库中加载到内存。我们在数据库中维护了权限信息,所以这里从数据库中加载资源权限,如果有需要,在系统权限变动时可以直接反馈到内存。
@Service
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
private PermissionMapper permissionMapper; /**
* 资源权限
*/
private volatile HashMap<String, Collection<ConfigAttribute>> urlPermMap = null; @PostConstruct
public void init() {
loadResourceDefine();
} /**
* 加载资源,初始化资源变量
*/
public void loadResourceDefine() {
urlPermMap = new HashMap<>;
...
} @Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl(); // 资源权限为空,初始化资源
if (null == urlPermMap) {
synchronized (MyFilterInvocationSecurityMetadataSource.class) {
if (null == urlPermMap) {
loadResourceDefine();
}
}
} return urlPermMap.get(url);
} @Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
} @Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
创建一个 FilterInvocationSecurityMetadataSource 接口的实现类,在内部定义一个Map用来维护资源权限信息,bean创建的时候初始化Map。然后重写getAttributes()方法,决策器会调用该方法获取url对应的权限。
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
* 决策方法:权限判断
*
* @param authentication 用户的身份信息;
* @param object 包含客户端发起的请求的request信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
* @param configAttributes 是MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,
* 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限;如果不在权限表中则放行。
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (Collections.isEmpty(configAttributes)) {
return;
}
for (GrantedAuthority ga : authentication.getAuthorities()) {
if (configAttributes.contains(ga.getAuthority())){
return;
}
}
throw new AccessDeniedException(StatusCodeEnum.UNAUTHORIZED.getValue());
} @Override
public boolean supports(ConfigAttribute attribute) {
return true;
} @Override
public boolean supports(Class<?> clazz) {
return true;
}
}
重写AccessDecisionManager 的decide()方法,在该方法中定义具体的判断逻辑,也可以通过定义投票器来实现。
验证过滤器的功能实际是通过依赖的资源权限和决策管理器来实现的,参照默认的验证过滤器FilterSecurityInterceptor来实现一个自定义验证过滤器。
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private MyFilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
/**
* @param fi 里面有一个被拦截的url,调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限,
* 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
* @throws IOException
* @throws ServletException
*/
public void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
这里创建一个自定义验证过滤器,然后将前面定义的MyFilterInvocationSecurityMetadataSource 和MyAccessDecisionManager 配置进来。最后还需要在系统配置文件中启用该验证过滤器。
@Configuration
@EnableWebSecurity // 禁用Spring Boot默认的Security配置,配合@Configuration启用自定义配置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
// 自定义决策管理器
@Autowired
private MyAccessDecisionManager myAccessDecisionManager; /*
* 加密工具
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} /*
* 认证管理器
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} /*
* 身份验证配置,用于注入自定义身份验证Bean和密码校验规则
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} /**
* 无需权限校验直接放行的路径
*/
private final String[] PATH_PASS = {
// 根据实际情况添加
}; /**
* Request层面的配置,对应XML Configuration中的<http>元素
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(PATH_PASS).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.httpBasic();
// 将自定义的过滤器配置在FilterSecurityInterceptor之前
http.addFilterBefore(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class);
} /**
* Web层面的配置,一般用来配置无需权限校验的路径,也可以在HttpSecurity中配置,但是在web.ignoring()中配置效率更高。
* web.ignoring()是一个忽略的过滤器,而HttpSecurity中定义了一个过滤器链,即使permitAll()放行还是会走所有的过滤器,
* 直到最后一个过滤器FilterSecurityInterceptor认定是可以放行的,才能访问。
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favor.ioc");
} /**
* 管理自定义的权限过滤器
*/
@Bean
public MyFilterSecurityInterceptor myFilterSecurityInterceptor() {
MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
myFilterSecurityInterceptor.setMyAccessDecisionManager(myAccessDecisionManager);
return myFilterSecurityInterceptor;
}
}
代码中注释标红的部分为配置自定义验证过滤器需要注意的地方。
* 这里是在授权服务器中实现的动态配置资源权限,在资源服务器中方法一样。
Spring Security之动态配置资源权限的更多相关文章
- spring security实现动态配置url权限的两种方法
缘起 标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色. 基于s ...
- [转]Spring Security 可动态授权RBAC权限模块实践
RBAC:基于角色的访问控制(Role-Based Access Control) 先在web.xml 中配置一个过滤器(必须在Struts的过滤器之前) <filter> <fil ...
- Spring Security实现基于RBAC的权限表达式动态访问控制
昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制.我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式,所以这个东西对我不陌生,但是在Spring Securit ...
- CAS Spring Security 3 整合配置(转)
一般来说, Web 应用的安全性包括用户认证( Authentication )和用户授权( Authorization )两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否 ...
- Spring Security基于Java配置
Maven依赖 <dependencies> <!-- ... other dependency elements ... --> <dependency> < ...
- Spring Security(三) —— 核心配置解读
摘要: 原创出处 https://www.cnkirito.moe/spring-security-3/ 「老徐」欢迎转载,保留摘要,谢谢! 3 核心配置解读 上一篇文章<Spring Secu ...
- spring boot rest 接口集成 spring security(2) - JWT配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- spring security中动态更新用户的权限
在程序的执行过程中,有时有这么一种需求,需要动态的更新某些角色的权限或某些人对应的权限,当前在线的用户拥有这个角色或拥有这个权限时,在不退出系统的情况下,需要动态的改变的他所拥有的权限. 需求:张三 ...
- Spring security 用户,角色,权限,资源
转自:http://blog.csdn.net/wybqq/article/details/52940194 关于Spring security对用户请求的处理过程 体现在这两个过程的体现. 关于用户 ...
随机推荐
- (摘录)String是值传递还是引用传递
String应该是一个封装类型,它应该是引用传递,是可以改变值的, 运行的结果应该是”cd”.我们实际运行一下看看, str=ab,这如何解释呢?难道String是基本类型?也说不通呀. 这就要从ja ...
- Linux---基础指令(一)
https://www.linuxprobe.com/chapter-02.html (Linux就要这么学) 一.执行查看帮助命令 date:date命令用于显示及设置系统的时间或日期,格式为“d ...
- 2019.03.26 bzoj4448: [Scoi2015]情报传递(归并排序+树链剖分)
传送门 题意简述: 给一棵nnn个点的树,树上每个点表示一个情报员,一共有mmm天,每天会派发以下两种任务中的一个任务: 1.搜集情报:指派T号情报员搜集情报 2.传递情报:将一条情报从X号情报员传递 ...
- (转载)sqlmap用户手册详解
文章转载自 http://www.vuln.cn/2035 当给sqlmap这么一个url (http://www.target.com/sqlmap/mysql/get_int.php?id=1) ...
- HDU 6346 整数规划 (最佳完美匹配)
整数规划 Time Limit: 5500/5000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Subm ...
- DataTransfer对象的一些总结
所有元素都支持防止目标事件,但是这些元素默认是不允许释放的,如果拖动元素经过不允许放置的元素无论用户如何操作,都不会触发drop事件,不过可以把任何元素变成有效的放置目标.方法是重写dropenter ...
- search
|—search()—|—添加一个列表变量Expend,存储每个小格扩展时为第几步,可打印出 | |—打印运动表 |—A*—|— heuristic() |—Dy ...
- Node.js创建本地简易服务器
创建简易的本地服务器 安装node.js 在项目下,通过npm init -y创建package.json文件 通过npm install mime --save加载mime插件 创建server.j ...
- PowerPoint 2013中创建自定义路径动画的方法
1.在幻灯片中选择对象,在“动画”选项卡的“高级动画”组中单击“添加动画”按钮,在打开的下拉列表中选择“自定义路径”选项,如图1所示. 图1 选择“自定义路径”选项 2.此时鼠标指针变为十字形,在幻灯 ...
- enumerate的用法
names = ["tony","huluwa","tom","jerry","lala"]for ...