java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > springboot security FilterSecurityInterceptor使用要点

springboot之security FilterSecurityInterceptor的使用要点记录

作者:一名工程师

这篇文章主要介绍了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也是.

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

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

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

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

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

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

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

总结

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

您可能感兴趣的文章:
阅读全文