第 36 章 拦截器

无论是Filter,MethodInterceptor,ACL都要用到AOP,实际上都是拦截器的概念,其中要用到AbstractSecurityInterceptor总拦截器,AfterInvocationManager后置拦截,authenticationManager验证管理器,可能还要用上RunAsManager。

关于RunAsManager,官方文档给出的解释是,在HttpInvoker或者Web Service的情况下,当前用户的一些身份要转换成其他身份,这时就是用RunAsManager,默认将以RUN_AS_开头的权限名,改变成 ROLE_RUN_AS_开头的权限名,然后重新赋予当前认证主体是用。现在的问题是不清楚具体使用在什么场景。

36.1. 权限配置数据源

处于继承树顶端的AbstractSecurityInterceptor有三个实现类:

  • FilterSecurityInterceptor,负责处理FilterInvocation,实现对URL资源的拦截。

  • MethodSecurityInterceptor,负责处理MethodInvocation,实现对方法调用的拦截。

  • AspectJSecurityInterceptor,负责处理JoinPoint,主要也是用于对方法调用的拦截。

为了限制用户访问被保护资源,Spring Security提供了一套元数据,用于定义被保护资源的访问权限,这套元数据主要体现为ConfigAttribute和Collection<ConfigAttribute>。每个ConfigAttribute中只包含一个字符串,而Collection<ConfigAttribute>包含多个ConfigAttribute。对于系统来说,每个被保护资源都将对应一组Collection<ConfigAttribute>,这个Collection<ConfigAttribute>中包含的多个ConfigAttribute就是访问该资源所需的权限。

实际应用中,Collection<ConfigAttribute>会保存在SecurityMetadataSource中,这是一个主要接口,FilterSecurityInterceptor所需的DefaultFilterInvocationSecurityMetadataSource和MethodSecurityInterceptor所需的MethodSecurityMetadataSource都实现了这个接口。SecurityMetadataSource可以看做是Spring Security中权限配置的源头,框架内部所有的验证组件都是从SecurityMetadataSource中获得数据,来对被保护资源进行权限控制的。

为了从xml中将用户配置的访问权限转换成SecurityMetadataSource类型的对象,Spring Security专门扩展了Spring中提供的intercept-url和intercept-methods两个标签来支持ConfigAttribute,它可以把以逗号分隔的一系列字符串转换成包含多个ConfigAttribute。

"ROLE_ADMIN,ROLE_USER"

↓

Collection<ConfigAttribute>
  ConfigAttribute["ROLE_ADMIN"]
  ConfigAttribute["ROLE_USER"]
        

对于FilterSecurityInterceptor来说,最终生成的就是一个包含了url pattern和ConfigAttributeConfiguration的ObjectDefinitionSource。


<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN,ROLE_USER" />

↓

					Collection<ConfigAttribute>
"/admin.jsp"   →     ConfigAttribute["ROLE_ADMIN"]
                      ConfigAttribute["ROLE_USER"]
        

换而言之,无论我们将权限配置的原始数据保存在什么地方,只要最终可以将其转换为SecurityMetadataSource就可以提供给验证组件进行调用,实现权限控制。

36.2. 权限管理器

AbstractSecurityInterceptor中将几个权限管理器组合应用,AuthenticationManager, AccessDecisionManager, AfterInvocationManager和RunAsManager。

AuthenticationManager用来对用户请求进行认证工作,默认情况下,我们使用的实现类为NamespaceAuthenticationManager。它内部将包含一个AuthenticationProvider队列,在实际进行权限校验的时候顺序执行这个队列实现对用户的认证功能。AuthenticationProvider中最常见的实现是DaoAuthenticationProvider,它可以根据用户的username和password对用户有效性进行验证,如果通过校验就会从userDetailsService中获取用户信息,并为用户授予对应的权限。

AccessDecisionManager用于控制资源的访问权限,它下面有三个实现类可以选择AffirmativeBased, UnanimousBased和ConsensusBased,分别对应,一票通过,一票否决,多数通过。每个AccessDecisionManager内部拥有多个Voter,每个Voter会进行表决,表决的结果有ACCESS_GRANTED赞成, ACCESS_DENIED反对, ACCESS_ABSTAIN弃权三种。AccessDecisionManager会根据最终投票的结果,结合实现类的策略判断用户是否可以访问当前资源。

36.3. 后置调用管理器

AfterInvocationManager用来在方法调用完成后,根据用户的权限,对方法返回的结果进行筛选,它主要是用在ACL中的。

AfterInvocationManager有两个实现类。BasicAclEntryAfterInvocationCollectionFilteringProvider用于过滤返回结果为集合的方法,BasicAclEntryAfterInvocationProvider用于控制返回结果为对象的方法。

36.4. 临时分配额外权限

我们可以在某一方法调用过程中,使用RunAsManager为用户临时分配额外的权限。据说这个功能是为了在方法内部远程调用被保护资源而实现的,为实现这一功能,我们首先要在AbstractMethodInterceptor中设置RunAsManagerImpl,并且要在ObjectDefintionSource中配置RUN_AS_开头的权限,这样,当用户访问这个方法时,就会自动将SecurityContext中保存的Authenticaton对象替换为RUN_AS对象,并在其中附加额外的权限。

3.1.3中提供了如下方法。

<security:global-method-security run-as-manager-ref="runAsManager">
    <protect-pointcut
        expression="execution(* com.family168.springsecuritybook.ch12.MesageServiceImpl.admin*(..))"
        access="ROLE_ADMIN,RUN_AS_MANAGER"/>
</security:global-method-security>