spring boot security FilterSecurityInterceptor 使用要点记录

spring security FilterSecurityInterceptor 使用要点记录

FilterSecurityInterceptor是一个方法级的权限过滤器, 基本位于过滤链的最底部
spring boot security FilterSecurityInterceptor 使用要点记录_第1张图片
该过滤器用于控制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 getAttributes(Object object) {
        FilterInvocation fi = (FilterInvocation) object;
            String url = fi.getRequestUrl();
            String httpMethod = fi.getRequest().getMethod();
            List attributes = new ArrayList();

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

            return attributes;
    }

    public Collection getAllConfigAttributes() {
        return null;
    }

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

具体思路就是通过自定义过滤器的MetadataSource来实现规则的灵活配置, 该部分实例默认使用的是DefaultFilterInvocationSecurityMetadataSource, 可以根据这里的源码来编写自己的MetadataSource.
内部使用下面这个结构来维护匹配规则和对应的权限集

Map> requestMap

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

RequestMatchers.antMatchers(...)

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

public static List antMatchers(HttpMethod httpMethod,
		String... antPatterns) {
	String method = httpMethod == null ? null : httpMethod.toString();
	List 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也是. 有这么几个其实写之前就该考虑好的问题.

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

其实spring官方是推荐方法级权限就直接硬编码的. 因为考虑到放在数据库后, 安全上的风险实在太大了. 仅仅通过修改数据库, 即使非admin角色的账户也是能获取所有的操作权限的. 另一点是操作权限定义上的变更(哪些角色该有哪些操作权限?)本身就应该是需要审计的, 并且非常低频的. 硬编码在排除风险之余, 对于实际使用的影响其实也是微乎其微的(无非每次确定要改了, 发一次版)
至于我为何要写自定义的FilterSecurityInterceptor, 主要是系统的security集成在路由层, 那边不定义方法, 法2在configure里硬编码好像又太繁琐, 所以想在Metadatasource层用文件或者什么静态常量的方法硬编码.
不过最后写完发现好像也不便利?并不快乐??? 为了这个目标多写了好多实现类, 并不能轻松愉快地直接注入Metadatasource.

如果有其他朋友找到了更加便捷的注入方式, 还请指明具体操作~不胜感激.

你可能感兴趣的:(java,spring,boot,security)