在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. 地磁应用中的低功耗无线数传模块xbee PRO S2C

    地球上每一个地理坐标点,在一段时间内磁场强度是恒定的,当车辆这种铁磁物质经过这个点时,对这个点的磁场强度产生一个连续的扰动,通过磁传感器采样数据与初始采样数据(该点的地球磁场值)进行对比,其差值为车辆 ...

  2. 【竞价网站绝技】根据访客ip,页面显示访客所属城市的html代码(借用YY IP地址库)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 2019.03.09 codeforces833B. The Bakery(线段树优化dp)

    传送门 线段树优化dpdpdp入门题. 要求把nnn个数分成kkk段,每段价值为里面不相同的数的个数,求所有段的价值之和最大值.n≤35000,k≤50n\le35000,k\le50n≤35000, ...

  4. spring 3.1.1 mvc HanderMapping源码

    https://my.oschina.net/zhangxufeng/blog/2177464 RequestMappingHandlerMapping getMappingForMethod /** ...

  5. (转)pycharm常用快捷键

    Alt+Enter 自动添加包Ctrl+t SVN更新Ctrl+k SVN提交Ctrl + / 注释(取消注释)选择的行Ctrl+Shift+F 高级查找Ctrl+Enter 补全Shift + En ...

  6. 将表格添加到Word文档中 ,包括表格样式设置

    创建 Table 对象并设置其属性 在您将表格插入文档之前,必须创建 Table 对象并设置其属性. 要设置表格的属性,请创建TableProperties对象并为其提供值. TablePropert ...

  7. javascript小括号、中括号、大括号学习总结

    作为一名编程人员,和括号打交道是必不可少的.你可知道在不同的上下文中,括号的作用是不一样的,今天就让我们简单总结下javascript小括号.中括号.大括号的用法. 总的来说,JavaScript中小 ...

  8. Sqlalchemy python经典第三方orm

    Ⅰ. 安装 pip install sqlalchemy Ⅱ. 起步链接 import time import threading import sqlalchemy from sqlalchemy ...

  9. Beta冲刺 (2/7)

    Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 熟悉并编写小程序的自定义控件 展示G ...

  10. Ubuntu里Eclipse关联Jdk

    Ubuntu里Eclipse关联Jdk 1.在Eclipse安装目录创建jre 文件. 2.ln  -s  jdk目录/bin  bin 输入上述命令,之后eclipse即可打开.