springboot之security FilterSecurityInterceptor的使用要点记录

 更新时间:2023年12月11日 10:17:06   作者:一名工程师  
这篇文章主要介绍了springboot之security FilterSecurityInterceptor的使用要点记录,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

spring security FilterSecurityInterceptor使用要点

FilterSecurityInterceptor是一个方法级的权限过滤器, 基本位于过滤链的最底部

该过滤器用于控制method级别的权限控制. 官方提供了2种默认的方法权限控制写法

一种是在方法上加注释实现,另一种是在configure配置中通过

@Secured("ROLE_ADMIN") //法1, 方法定义处加注释, 需先在具体的配置里开启此类配置
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
	
法2, 在复写的configure里直接定义
.antMatchers("your match rule").authenticated()
.antMatchers("your match rule").hasRole("ADMIN") //使用时权限会自动加前缀ROLE_ADMIN

具体细节的代码就不贴了,官方文档一模一样的都有.

上面两种方法最终都会生成一个FilterSecurityInterceptor实例,放在上面过滤链底部. 用于方法级的鉴权.

官方还提到了第三种方法,关于如何把过滤的规则放到更为灵活的位置,数据库/本地文件/等等.

贴一段官方代码

public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	//此方法用于鉴权过程中获取当前的请求URL需要哪种权限
    public List<ConfigAttribute> getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
            String url = fi.getRequestUrl();
            String httpMethod = fi.getRequest().getMethod();
            List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

            // Lookup your database (or other source) using this information and populate the
            // list of attributes

            return attributes;
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

具体思路就是通过自定义过滤器的MetadataSource来实现规则的灵活配置,该部分实例默认使用的是DefaultFilterInvocationSecurityMetadataSource, 可以根据这里的源码来编写自己的MetadataSource.

内部使用下面这个结构来维护匹配规则和对应的权限集

Map<RequestMatcher, Collection<ConfigAttribute>> requestMap

RequestMatcher和ConfigAttribute都是抽象类,需要找个能用的类,通过查阅源码可以找到RequestMathcer的具体构造实现

RequestMatchers.antMatchers(...)

不过该方法不能直接拿来用,写了私有

public static List<RequestMatcher> antMatchers(HttpMethod httpMethod,
		String... antPatterns) {
	String method = httpMethod == null ? null : httpMethod.toString();
	List<RequestMatcher> matchers = new ArrayList<>();
	for (String pattern : antPatterns) {
		matchers.add(new AntPathRequestMatcher(pattern, method));
	}
	return matchers;
}

改用new AntPathRequestMatcher(pattern, method)这个就行

ConfigAttribute有一个叫SecurityConfig的实例, 构造时传入String就可以构造对应的权限实例

不过官方好像没写具体怎么注入这个自定义的MetadataSource???

找了半天好像都没找到能直接注入到默认的Filter的途径, 没办法只能写一个新的自定义FilterSecurityInterceptor来注入, 通过configure里的addFilter()方法放入过滤链

setSecurityMetadataSource()方法写入自定义的rules源, 还需要注入AccessDecision和Authentication,

前者用于验证访问权限, 继承AccessDecisionManager实现decide方法来编写自定义的验证逻辑

decide方法包含 Authentication, 一个Object类型的FilterInvocation实例, 一组ConfigAttribute(要求的权限列表, 从MetaSource的getAttributes()方法里取的, 完整的获取和校验上层逻辑都封装在AbstractSecurityInterceptorbeforeInvocation()方法里)

后者用于验证登录授权, 后者使用默认的super.authenticationManager()即可

完成自定义方法级过滤后碰到几个问题,一个是加入了这个Filter后原先方法1和方法2设置的就都失效了.

这里直接说看源码打断点后的结论, 主要是因为自定义的filter加入后, 和原先的默认FilterSecurityInterceptor会有互相排斥的问题, 具体表现为只要这两个中的其中一个先执行invoke()方法, 就会在request里追加一个名为__spring_security_filterSecurityInterceptor_filterApplied的attribute表示FilterSecurityInterceptor这个类型的过滤器已经执行过了. 当另一个同类的FilterSecurityInterceptor进来时就直接跳过具体的invoke方法直接执行下一个过滤器了.

过滤器的位置排序上, addFilter()加的自定义FilterSecurityInterceptor排到了默认的FilterSecurityInterceptor之前,如果要放在默认的后面, 用addFilterAfter()方法, 指定需要放在哪个过滤器后面.

所以对应的解决办法也很简单,覆写自定义过滤器中的invoke方法,把加attribute那段去掉.

我就不处理这个问题了, 其实这个地方算不算一个问题还得单独考虑的, 包括上面自定义Metadata也是.

有这么几个其实写之前就该考虑好的问题.

  • 系统里的方法级权限真的需要通过数据库灵活配置吗?
  • 系统真的需要让自定义的filter和默认filter的权限规则同时生效吗?

其实spring官方是推荐方法级权限就直接硬编码的. 因为考虑到放在数据库后, 安全上的风险实在太大了.

仅仅通过修改数据库,即使非admin角色的账户也是能获取所有的操作权限的.

另一点是操作权限定义上的变更(哪些角色该有哪些操作权限?)本身就应该是需要审计的,并且非常低频的.

硬编码在排除风险之余,对于实际使用的影响其实也是微乎其微的(无非每次确定要改了,发一次版)

至于我为何要写自定义的FilterSecurityInterceptor, 主要是系统的security集成在路由层,那边不定义方法,法2在configure里硬编码好像又太繁琐,所以想在Metadatasource层用文件或者什么静态常量的方法硬编码.

不过最后写完发现好像也不便利?并不快乐??? 为了这个目标多写了好多实现类,并不能轻松愉快地直接注入Metadatasource.

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 关于QueryWrapper,实现MybatisPlus多表关联查询方式

    关于QueryWrapper,实现MybatisPlus多表关联查询方式

    这篇文章主要介绍了关于QueryWrapper,实现MybatisPlus多表关联查询方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。
    2022-01-01
  • Java 8 Stream操作类型及peek示例解析

    Java 8 Stream操作类型及peek示例解析

    这篇文章主要介绍了Java 8 Stream操作类型及peek示例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Spring boot详解缓存redis实现定时过期方法

    Spring boot详解缓存redis实现定时过期方法

    本篇文章分享的就是spring boot中的一个轮子,spring cache注解的方式实现接口数据缓存。默认的配置想非常简单,但是有一个弊端是缓存数据为永久缓存,本次将介绍如何设置接口缓存数据的过期时间
    2022-07-07
  • Maven-POM文件及组成部分

    Maven-POM文件及组成部分

    POM是用于描述Maven项目的配置文件,它包含了项目构建、依赖管理和其他相关配置的信息,这篇文章主要介绍了Maven-POM文件,需要的朋友可以参考下
    2023-06-06
  • Java导出txt文件的方法

    Java导出txt文件的方法

    这篇文章主要介绍了Java导出txt文件的方法,实例分析了两种java导出txt文本文件的使用技巧,需要的朋友可以参考下
    2015-05-05
  • Spring Boot中自动执行sql脚本的实现

    Spring Boot中自动执行sql脚本的实现

    这篇文章主要介绍了Spring Boot中自动执行sql脚本的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Java中 SLF4J和Logback和Log4j和Logging的区别与联系

    Java中 SLF4J和Logback和Log4j和Logging的区别与联系

    这篇文章主要介绍了Java中 SLF4J和Logback和Log4j和Logging的区别与联系,文章通过围绕主题展开详细的内容介绍,具有一定的参考几种,感兴趣的小伙伴可以参考一下
    2022-09-09
  • Spring详解使用注解开发流程

    Spring详解使用注解开发流程

    这篇文章主要为大家详细介绍了Spring如何使用注解开发,文中的示例代码讲解详细,对我们学习或工作有一定帮助,需要的可以参考一下
    2022-05-05
  • Java模拟实现斗地主的洗牌和发牌

    Java模拟实现斗地主的洗牌和发牌

    这篇文章主要为大家详细介绍了Java模拟实现斗地主的洗牌和发牌,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Java通过动态规划设计股票买卖最佳时机

    Java通过动态规划设计股票买卖最佳时机

    动态规划可谓是大名鼎鼎,笔试面试中的高频考点,也是重点难点,动态规划类型题目灵活多变,难度系数也相对较高,往往我们做不好动态规划的题目就会与心仪的offer失之交臂,本篇文章我们就一起来研究一下动态规划设计股票买卖最佳时机
    2022-10-10

最新评论