内容简介:Spring Security的权限鉴定是由AccessDecisionManager接口负责的。具体来说是由其中的decide()方法负责,其定义如下。
Spring Security的权限鉴定是由AccessDecisionManager接口负责的。具体来说是由其中的decide()方法负责,其定义如下。
void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException;
如你所见,该方法接收三个参数,第一个参数是包含当前用户信息的Authentication对象;第二个参数表示当前正在请求的受保护的对象,基本上来说是MethodInvocation(使用AOP)、JoinPoint(使用Aspectj)和FilterInvocation(Web请求)三种类型;第三个参数表示与当前正在访问的受保护对象的配置属性,如一个角色列表。
1.1 Spring Security的AOP Advice思想
对于使用AOP而言,我们可以使用几种不同类型的advice:before、after、throws和around。其中around advice是非常实用的,通过它我们可以控制是否要执行方法、是否要修改方法的返回值,以及是否要抛出异常。Spring Security在对方法调用和Web请求时也是使用的around advice的思想。在方法调用时,可以使用标准的Spring AOP来达到around advice的效果,而在进行Web请求时是通过标准的Filter来达到around advice的效果。
对于大部分人而言都比较喜欢对Service层的方法调用进行权限控制,因为我们的主要业务逻辑都是在Service层进行实现的。如果你只是想保护Service层的方法,那么使用Spring AOP就可以了。如果你需要直接保护领域对象,那么你可以考虑使用Aspectj。
你可以选择使用Aspectj或Spring AOP对方法调用进行鉴权,或者选择使用Filter对Web请求进行鉴权。当然,你也可以选择使用这三种方式的任意组合进行鉴权。通常的做法是使用Filter对Web请求进行一个比较粗略的鉴权,辅以使用Spring AOP对Service层的方法进行较细粒度的鉴权。
1.2 AbstractSecurityInterceptor
AbstractSecurityInterceptor是一个实现了对受保护对象的访问进行拦截的抽象类,其中有几个比较重要的方法。beforeInvocation()方法实现了对访问受保护对象的权限校验,内部用到了AccessDecisionManager和AuthenticationManager;finallyInvocation()方法用于实现受保护对象请求完毕后的一些清理工作,主要是如果在beforeInvocation()中改变了SecurityContext,则在finallyInvocation()中需要将其恢复为原来的SecurityContext,该方法的调用应当包含在子类请求受保护资源时的finally语句块中;afterInvocation()方法实现了对返回结果的处理,在注入了AfterInvocationManager的情况下默认会调用其decide()方法。AbstractSecurityInterceptor只是提供了这几种方法,并且包含了默认实现,具体怎么调用将由子类负责。每一种受保护对象都拥有继承自AbstractSecurityInterceptor的拦截器类, MethodSecurityInterceptor将用于调用受保护的方法,而FilterSecurityInterceptor将用于受保护的Web请求。它们在处理受保护对象的请求时都具有一致的逻辑,具体的逻辑如下。
1、先将正在请求调用的受保护对象传递给beforeInvocation()方法进行权限鉴定。
2、权限鉴定失败就直接抛出异常了。
3、鉴定成功将尝试调用受保护对象,调用完成后,不管是成功调用,还是抛出异常,都将执行finallyInvocation()。
4、如果在调用受保护对象后没有抛出异常,则调用afterInvocation()。
以下是MethodSecurityInterceptor在进行方法调用的一段核心代码。
public Object invoke(MethodInvocation mi) throws Throwable {
InterceptorStatusToken token = super .beforeInvocation(mi);
Object result;
try {
result = mi.proceed();
} finally {
super .finallyInvocation(token);
}
return super .afterInvocation(token, result);
}
1.2.1 ConfigAttribute
AbstractSecurityInterceptor的beforeInvocation()方法内部在进行鉴权的时候使用的是注入的AccessDecisionManager的decide()方法进行的。如前所述,decide()方法是需要接收一个受保护对象对应的ConfigAttribute集合的。一个ConfigAttribute可能只是一个简单的角色名称,具体将视AccessDecisionManager的实现者而定。AbstractSecurityInterceptor将使用一个SecurityMetadataSource对象来获取与受保护对象关联的ConfigAttribute集合,具体SecurityMetadataSource将由子类实现提供。ConfigAttribute将通过注解的形式定义在受保护的方法上,或者通过access属性定义在受保护的URL上。例如我们常见的<intercept-url pattern=”/**” access=”ROLE_USER,ROLE_ADMIN”/>就表示将ConfigAttribute ROLE_USER和ROLE_ADMIN应用在所有的URL请求上。对于默认的AccessDecisionManager的实现,上述配置意味着用户所拥有的权限中只要拥有一个GrantedAuthority与这两个ConfigAttribute中的一个进行匹配则允许进行访问。当然,严格的来说ConfigAttribute只是一个简单的配置属性而已,具体的解释将由AccessDecisionManager来决定。
1.2.2 RunAsManager
在某些情况下你可能会想替换保存在SecurityContext中的Authentication。这可以通过RunAsManager来实现的。在AbstractSecurityInterceptor的beforeInvocation()方法体中,在AccessDecisionManager鉴权成功后,将通过RunAsManager在现有Authentication基础上构建一个新的Authentication,如果新的Authentication不为空则将产生一个新的SecurityContext,并把新产生的Authentication存放在其中。这样在请求受保护资源时从SecurityContext中获取到的Authentication就是新产生的Authentication。待请求完成后会在finallyInvocation()中将原来的SecurityContext重新设置给SecurityContextHolder。AbstractSecurityInterceptor默认持有的是一个对RunAsManager进行空实现的NullRunAsManager。此外,Spring Security对RunAsManager有一个还有一个非空实现类RunAsManagerImpl,其在构造新的Authentication时是这样的逻辑:如果受保护对象对应的ConfigAttribute中拥有以“RUN_AS_”开头的配置属性,则在该属性前加上“ROLE_”,然后再把它作为一个GrantedAuthority赋给将要创建的Authentication(如ConfigAttribute中拥有一个“RUN_AS_ADMIN”的属性,则将构建一个“ROLE_RUN_AS_ADMIN”的GrantedAuthority),最后再利用原Authentication的principal、权限等信息构建一个新的Authentication进行返回;如果不存在任何以“RUN_AS_”开头的ConfigAttribute,则直接返回null。RunAsManagerImpl构建新的Authentication的核心代码如下所示。
public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
List<GrantedAuthority> newAuthorities = new ArrayList<GrantedAuthority>();
for (ConfigAttribute attribute : attributes) {
if ( this .supports(attribute)) {
GrantedAuthority extraAuthority = new SimpleGrantedAuthority(getRolePrefix() + attribute.getAttribute());
newAuthorities.add(extraAuthority);
}
}
if (newAuthorities.size() == 0) {
return null ;
}
// Add existing authorities
newAuthorities.addAll(authentication.getAuthorities());
return new RunAsUserToken( this .key, authentication.getPrincipal(), authentication.getCredentials(),
newAuthorities, authentication.getClass());
}
1.2.3 AfterInvocationManager
在请求受保护的对象完成以后,可以通过afterInvocation()方法对返回值进行修改。AbstractSecurityInterceptor把对返回值进行修改的控制权交给其所持有的AfterInvocationManager了。AfterInvocationManager可以选择对返回值进行修改、不修改或抛出异常(如:后置权限鉴定不通过)。
(注:本文是基于Spring Security3.1.6所写)
springsecurity Run-As认证服务
<security:authentication-manager alias=“authenticationManager”> <security:authentication-provider ref=“daoAuthenticationProvider” /> <security:authentication-provider ref=“runAsImplAuthenticationProvider” /> </security:authentication-manager>
<bean id=“methodSecurityInterceptor” class=“org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor”> <property name=“authenticationManager” ref=“authenticationManager” /> <property name=“accessDecisionManager” ref=“accessDecisionManager” /> <property name=“securityMetadataSource” ref=“delegatingMethodSecurityMetadataSource” /> <property name=“runAsManager” ref=“runAsManager” /> <property name=“objectDefinitionSource”> <value> zhangxin.security.service.RoleService.loadMenu=ROLE_USER, RUN_AS_TEMP zhangxin.security.RunAsDemo.RunAsDate.showDate=ROLE_ADMIN, ROLE_RUN_AS_TEMP </value> </property> </bean>
<!– 替换验证身份 –> <bean id=“runAsImplAuthenticationProvider” class=“org.springframework.security.access.intercept.RunAsImplAuthenticationProvider”> <property name=“key” value=“javaee” /> </bean> <bean id=“runAsManager” class=“org.springframework.security.access.intercept.RunAsManagerImpl”> <property name=“key” value=“javaee” /> </bean>
objectDefinitionSource 与 delegatingMethodSecurityMetadataSource 一样,都为获取的资源。
以上配置
zhangxin.security.service.RoleService.loadMenu 简称方法A
zhangxin.security.RunAsDemo.RunAsDate.showDate 简称方法B
用户要访问方法B,而用户拥有的角色为ROLE_USER,
访问方法B,需要角色为ROLE_RUN_AS_TEMP,
那么此时就必须使用户拥有角色ROLE_RUN_AS_TEMP,才能访问方法B。
关键点再这里:
访问方法A的过程中会拥有以’RUN_AS’开头的RUN_AS_TEMP角色,实际过程中还会再加上角色前缀’ROLE_’,也就是ROLE_RUN_AS_TEMP角色。
该用户在拥有角色ROLE_USER在执行方法A的过程中,拥有ROLE_RUN_AS_TEMP角色,在执行方法A过程中可以调用方法B。
注意: 用户执行方法A之前,没有ROLE_RUN_AS_TEMP角色,执行方法过程中才有,执行完毕后,自动移除ROLE_RUN_AS_TEMP角色。
—–
替换验证身份
13.1. 概述
AbstractSecurityInterceptor
可以在安全对象回调期间,暂时替换 SecurityContext
和 SecurityContextHolder
里的 Authentication
对象。 只有在原始 Authentication
对象被 AuthenticationManager
和 AccessDecisionManager
成功处理之后,才有可能发生这种情况。 如果有需要, RunAsManager
会显示替换的 Authentication
对象,这应该通过 SecurityInterceptorCallback
调用。
通过在安全对象回调过程中临时替换 Authentication
对象,安全调用可以调用其他需要不同认证授权证书的对象。 这也可以为特定的 GrantedAuthority
对象执行内部安全检验。 因为Spring Security提供不少帮助类,能够基于 SecurityContextHolder
的内容自动配置远程协议,这些运行身份替换在远程web服务调用的时候特别有用。
13.2. 配置
一个Spring Security提供的 RunAsManager
接口::
Authentication buildRunAs(Authentication authentication, Object object, List<ConfigAttributeDefinition> config); boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);
第一个方法返回 Authentication
对象,在方法的调用期间替换以前的 Authentication
对象。 如果方法返回 null
,意味着不需要进行替换。 第二个方法用在 AbstractSecurityInterceptor
中,作为它启动时校验配置属性的一部分。 supports(Class)
方法会被安全拦截器的实现调用,确保配置的 RunAsManager
支持安全拦截器即将执行的安全对象类型。
Spring Security提供了一个 RunAsManager
的具体实现。 如果任何一个 ConfigAttribute
是以 RUN_AS_
开头的, RunAsManagerImpl
类返回一个替换的 RunAsUserToken
。 如果找到了任何这样的 ConfigAttribute
,替换的 RunAsUserToken
会通过一个新的 GrantedAuthorityImpl
,为每一个 RUN_AS_
ConfigAttribute
包含同样的主体,证书,赋予的权限,就像原来的 Authentication
对象一样。 每个新 GrantedAuthorityImpl
会以 ROLE_
开头,对应 RUN_AS
ConfigAttribute
。 比如,一个替代 RunAsUserToken
,对于 RUN_AS_SERVER
的结果是包含一个 ROLE_RUN_AS_SERVER
赋予的权限。
替代的 RunAsUserToken
就像其他 Authentication
对象一样。 它可能需要通过代理合适的 AuthenticationProvider
被 AuthenticationManager
验证。 这个 RunAsImplAuthenticationProvider
执行这样的认证,它直接获得任何一个有效的 RunAsUserToken
。
为了保证恶意代码不会创建一个 RunAsUserToken
,由 RunAsImplAuthenticationProvider
保障获得一个key的散列值被保存在所有生成的标记里。 RunAsManagerImpl
和 RunAsImplAuthenticationProvider
在bean上下文里,创建使用同样的key:
<bean id="runAsManager" class="org.springframework.security.access.intercept.RunAsManagerImpl"> <property name="key" value="my_run_as_password"/> </bean> <bean id="runAsAuthenticationProvider" class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider"> <property name="key" value="my_run_as_password"/> </bean>
通过使用相同的key,每个 RunAsUserToken
可以被它验证,并使用对应的 RunAsManagerImpl
创建。 出于安全原因,这个 RunAsUserToken
创建后就不能改变。
(注:原创文章,转载请注明出处。原文地址:http://elim.iteye.com/blog/2211966)
以上所述就是小编给大家介绍的《权限鉴定基础》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 司法鉴定牵手深度学习:Kaggle 相机型号识别大赛深度分析
- Buf早餐铺丨三星为其智能电视添加恶意软件扫描程序;黑客组织瞄准美国电网;平安保险利用AI鉴定客户
- Django框架–权限代码+左侧菜单和权限应用
- 同等权限下多任职之间数据权限的实例
- 在 Windows 系统上降低 UAC 权限运行程序(从管理员权限降权到普通用户权限)
- 在 Windows 系统上降低 UAC 权限运行程序(从管理员权限降权到普通用户权限)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。