在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. sqlserver 分割字符串和调用

    传入某种规则拼接字符串获得数组(表) /*功能说明:传入字符串跟分割符('''SGHE00000003'',''SGHE00000004'',''SGHE00000005'''),返回一个Table* ...

  2. mpdf-html转PDF,中文字符乱码、加粗问题

    $defaultConfig = (new ConfigVariables())->getDefaults(); $fontDirs = $defaultConfig['fontDir']; $ ...

  3. Liang-Barsky直线段裁剪算法

    Liang-Barsky直线段裁剪算法 梁友栋与Barsky提出的裁剪算法以直线的参数方程为基础,把判断直线段与窗口边界求交的 二维裁剪问题转化为求解一组不等式,确定直线段参数的一维裁剪问题.设起点为 ...

  4. Kalman Filters

    |—定位—|—蒙特卡洛方法(定位自身) |              |—卡尔曼滤波器(定位其他车辆) |—高斯函数 |—循环两个过程—|—测量(测量更新) |                     ...

  5. java面试一、1.4锁机制

    免责声明:     本文内容多来自网络文章,转载为个人收藏,分享知识,如有侵权,请联系博主进行删除. 1.3.锁机制 说说线程安全问题,什么是线程安全,如何保证线程安全 线程安全:当多个线程访问某一个 ...

  6. Visual Studio Code and local web server

    It is the start of a New Year and you have decided to try Visual Studio Code, good resolution! One o ...

  7. Django积木块七——视频

    视频 # 在网上搜索video.js然后下载相关的js和css文件,看文档正确使用视频模块,添加视频外链 <div style="width: 1200px;height: 675px ...

  8. const_cast的用法与测试

    在C++里,把常量指针(即指向长脸的指针)赋值给非常量指针时,会提示错误,这时候就需要用到const_cast,看下面的两个转换情形: int j = 0; const int i = j; int ...

  9. [升级说明] Senparc.Weixin.MP v14.8.11 (微信群发接口调整)

    升级内容:添加根据标签群发接口,重构原根据分组群发接口  参考微信文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp14811 ...

  10. mvc网站迁移.net core记录

    接口return Json()时序列号小写的问题 在Startup.cs->ConfigureServices方法配置一下解决 public void ConfigureServices(ISe ...