在Spring Security中实现通过数据库动态配置url资源权限,需要通过配置验证过滤器来实现资源权限的加载、验证。系统启动时,到数据库加载系统资源权限列表,当有请求访问时,通过对比系统资源权限列表和用户资源权限列表(在用户登录时添加到用户信息中)来判断用户是否有该url的访问权限。

  在配置验证过滤器时需要的配置项有如下几个:

  • filterSecurityInterceptor:通过继承AbstractSecurityInterceptor并实现Filter接口自定义一个验证过滤器,替换默认验证过滤器。
  • accessDecisionManager:通过实现AccessDecisionManager接口自定义一个决策管理器,判断是否有访问权限。判断逻辑可以写在决策管理器的决策方法中,也可以通过投票器实现,除了框架提供的三种投票器还可以添加自定义投票器。自定义投票器通过实现AccessDecisionVoter接口来实现。
  • securityMetadataSource:实现FilterInvocationSecurityMetadataSource接口,在实现类中加载资源权限,并在filterSecurityInterceptor中注入该实现类。
  • WebSecurityConfig:系统配置类,需要在配置类中配置启用filterSecurityInterceptor。

  

一、配置securityMetadataSource

  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对应的权限。

二、配置accessDecisionManager
@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

  验证过滤器的功能实际是通过依赖的资源权限和决策管理器来实现的,参照默认的验证过滤器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 配置进来。最后还需要在系统配置文件中启用该验证过滤器。

四、配置WebSecurityConfig
@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之动态配置资源权限的更多相关文章

  1. spring security实现动态配置url权限的两种方法

    缘起 标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色. 基于s ...

  2. [转]Spring Security 可动态授权RBAC权限模块实践

    RBAC:基于角色的访问控制(Role-Based Access Control) 先在web.xml 中配置一个过滤器(必须在Struts的过滤器之前) <filter> <fil ...

  3. Spring Security实现基于RBAC的权限表达式动态访问控制

    昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制.我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式,所以这个东西对我不陌生,但是在Spring Securit ...

  4. CAS Spring Security 3 整合配置(转)

    一般来说, Web 应用的安全性包括用户认证( Authentication )和用户授权( Authorization )两个部分.用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否 ...

  5. Spring Security基于Java配置

    Maven依赖 <dependencies> <!-- ... other dependency elements ... --> <dependency> < ...

  6. Spring Security(三) —— 核心配置解读

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-3/ 「老徐」欢迎转载,保留摘要,谢谢! 3 核心配置解读 上一篇文章<Spring Secu ...

  7. spring boot rest 接口集成 spring security(2) - JWT配置

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  8. spring security中动态更新用户的权限

    在程序的执行过程中,有时有这么一种需求,需要动态的更新某些角色的权限或某些人对应的权限,当前在线的用户拥有这个角色或拥有这个权限时,在不退出系统的情况下,需要动态的改变的他所拥有的权限. 需求:张三 ...

  9. Spring security 用户,角色,权限,资源

    转自:http://blog.csdn.net/wybqq/article/details/52940194 关于Spring security对用户请求的处理过程 体现在这两个过程的体现. 关于用户 ...

随机推荐

  1. 开发HR人事考试系统介绍

    确定好需要开发的模块以及功能 一套人事考试系统主要模块: 1)组织管理:公司部门成员信息 2)人事管理:人事信息,离职管理,职务管理,岗位管理: 3)考勤管理:班次设置,停工放假,假日设置,刷卡记录, ...

  2. Appium系列文章(1)获取appPackage和appActivity

    appPackage和appActivity 进行appium自动化测试非常重要的两个参数,我们所测试的APP不同,这两个参数肯定也是不一样的.那如何快速的获取这APP的这两个参数呢? 通过cmd命令 ...

  3. python pandas.DataFrame.append

    1.使用append首先要注意的是,你要合并两个DataFrame的columns即列名是否是相同的,不相同的就会报错. 2.我们会发现DataFrame的列名是不能够重复的,而行名(index)是可 ...

  4. tab选项卡--jq

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. eclipse启动时要求高版本jdk的问题

    在eclipse.ini文件首行添加 -vm C:\Program Files\Java\jdk1.8\jdk1.8.0_131\bin https://blog.csdn.net/wanlin77/ ...

  6. jmeter+ant+jenkins接口自动化环境搭建

    一.准备   1.安装jdk  --->点击查看安装教程 2.安装jmeter  ,官网下载jmeter解压,配置jmeter环境: JMETER_HOME (解压jdk路径) Path (;% ...

  7. ES6 generators in depth 一(译)

    今天在学习redux-saga时,外部链接推荐了这篇文章ES6 generators in depth,所以翻译的同时也可以加深一下对Generator的理解. 这里对原文一些只能在高版本现代浏览器使 ...

  8. python data science handbook1

    import numpy as np import matplotlib.pyplot as plt import seaborn; seaborn.set() rand = np.random.Ra ...

  9. Python之旅Day15 Bootstrap与Django初识

    Bootstrap初识 Bootstrap简介 Bootstrap 是最受欢迎的 HTML.CSS 和 JS 框架,用于开发响应式布局.移动设备优先的 WEB 项目.(官网http://www.boo ...

  10. 2.SpringMVC注解开发

    1.创建SpringMVC项目 配置web.xml <?xml version="1.0" encoding="UTF-8"?> <web-a ...