权限鉴定基础

栏目: Java · 发布时间: 6年前

内容简介: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 可以在安全对象回调期间,暂时替换 SecurityContextSecurityContextHolder 里的 Authentication 对象。 只有在原始 Authentication 对象被 AuthenticationManagerAccessDecisionManager 成功处理之后,才有可能发生这种情况。 如果有需要, 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 对象一样。 它可能需要通过代理合适的 AuthenticationProviderAuthenticationManager 验证。 这个 RunAsImplAuthenticationProvider 执行这样的认证,它直接获得任何一个有效的 RunAsUserToken

为了保证恶意代码不会创建一个 RunAsUserToken ,由 RunAsImplAuthenticationProvider 保障获得一个key的散列值被保存在所有生成的标记里。 RunAsManagerImplRunAsImplAuthenticationProvider 在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)


以上所述就是小编给大家介绍的《权限鉴定基础》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人人都在晒,凭什么你出彩

人人都在晒,凭什么你出彩

【美】奥斯丁•克莱恩 / 张舜芬、徐立妍 / 北京联合出版公司 / 2015-4 / 38.00

1. 《纽约时报》、亚马逊畅销书排名第1位、好评如潮的创意营销书。《出版人周刊》称其在社交网络时代“在安全范围内提供了实用的自我营销策略”。 2. TED演讲者创意分享:晒对了,全世界都为你点赞:别人在朋友圈、微博晒自拍、晒孩子、晒吃喝,你来晒创意、晒灵感、晒工作、晒收获,发出自己的声音,找到伙伴,机会也会主动找上门! 3. 10堂创意课+手绘涂鸦,所有人都能轻松读完、迅速学会的创意小......一起来看看 《人人都在晒,凭什么你出彩》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具