Spring Security之授权

  • 授权
    • 授权架构
      • 授权
      • 调用前置处理
        • AccessDecisionManager
        • 基于投票的AccessDecisionManager实现
          • RoleVoter
          • AuthenticatedVoter
          • 自定义Voter
      • 调用后处理
      • 分层角色
    • 安全对象实现
      • AOP Alliance (MethodInvocation) 安全拦截器
        • 显式MethodSecurityInterceptor配置
      • AspectJ (JoinPoint)安全拦截器
    • 基于表达式访问控制

授权

Spring Security中的高级授权功能是其如此受欢迎的原因之一。不管你选择使用哪种方式认证,授权服务都可以以一致且简单的方式在应用中被使用。

这一部分将介绍不同的AbstractSecurityInterceptor实现。然后,继续探讨如何通过使用域访问控制列表来微调授权。

授权架构

授权

所有的Authentication实现都存储了GrantedAuthority对象的列表。这些对象表示当前主体被授予的权限。AuthenticationManagerGrantedAuthority对象插入到Authentication中,之后AccessDecisionManager会读取这些权限并作出权限决策。

GrantedAuthority接口只有一个方法:

String getAuthority();

此方法允许AccessDecisionManager获取以精确的String形式表示的GrantedAuthority。大多数AccessDecisionManager都可以轻松地读取GrantedAuthority。如果某个GrantedAuthority不能被字符串精确的表示,那么它就是“复杂的”,那么它的getAuthority方法应该返回null

“复杂”GrantedAuthority的一个示例是,存储了一系列应用不同用户账户的操作和权限阈值。以一个字符串代表此类授权很困难,所以getAuthority方法应该返回null。意味着某个AccessDecisionManager专门支持这个GrantedAuthority实现,并能解析其方法的返回值。

SS自带一个GrantedAuthority实现,即SimpleGrantedAuthority。它允许任何用户指定的字符串转换成GrantedAuthority。所有的AuthenticationProvider都包含使用这个实现来填充Authentication对象的安全架构。

调用前置处理

SS提供了对安全对象(例如,方法执行或者web请求)访问进行控制的拦截器。AccessDecisionManager执行调用前决策,决定是否允许该调用。

AccessDecisionManager

AccessDecisionManagerAbstractSecurityInterceptor调用,它负责做出最后的访问控制决策。AccessDecisionManager包含三个方法:

void decide(Authentication authentication, Object secureObject,
    Collection attrs) throws AccessDeniedException;

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

decide方法接受所有与授权决策相关信息的参数。特别的,安全Object对象的传递可以检查实际安全对象中包含的参数。例如,假设安全对象是MethodInvocation,从这个对象中获取任何Customer参数很容易,然后在AccessDecisionManager实现某种安全逻辑确保主体被允许操作此Customer。如果访问被拒绝,实现应该抛出一个AccessDeniedException

supports(ConfigAttribute)方法被AbstractSecurityInterceptor在一开始调用来决定AccessDecisionManager是否能处理传入的ConfigAttributesupports(Class)方法被安全拦截器实现调用以确保配置的AccessDecisionManager支持拦截器中的某种类型的安全对象。

基于投票的AccessDecisionManager实现

虽然用户可以实现自己的AccessDecisionManager来控制授权的各个方面,但SS还是提供了若干基于投票的AccessDecisionManager实现,下图展示了相关的类:



如上,授权决定会轮询一系列的AccessDecisionVoter实现。AccessDecisionManager决定是否根据这些投票的评估抛出AccessDeniedException异常。

接口AccessDecisionVoter有三个方法:

int vote(Authentication authentication, Object object, Collection attrs);

boolean supports(ConfigAttribute attribute);

boolean supports(Class clazz);

具体的实现返回一个int值,取值为AccessDecisionVoter中的静态字段ACCESS_ABSTAINACCESS_DENIEDACCESS_GRANTED。若某实现对授权没有意见可以返回ACCESS_ABSTAIN,如果有意见则返回其他两个值中的一个。

SS提供了三个具体的AccessDecisionManager来计算投票。ConsensusBased实现基于非弃权投票的共识来允许或拒绝访问,在投票相等或者都是弃权的时候也有相关的属性控制行为。AffirmativeBased实现在有一个或多个ACCESS_GRANTED票的时候允许访问(即,如果有至少一个同意票,反对票会被忽略),类似ConsensusBased实现,当所有的票都是弃权票的时候,也有一个参数控制行为。UnanimousBased实现期望所有投票都为ACCESS_GRANTED票来允许访问,忽略弃权票,如果有反对票则拒绝访问,像其他实现一样,此实现也有一个参数控制所有票都为弃权票时的行为。

也可以自己实现AccessDecisionManager来以不同的方式计算投票。例如,特定的AccessDecisionVoter的投票会有额外的权重,而特定的投票人的反对票有一票否决的作用。

RoleVoter

SS中最常用AccessDecisionVoter实现就是RoleVoter,它将配置属性当做简单的角色名,当用户有这个角色的时候允许访问。

如果任何ConfigAttributeROLE_开头,它将投票。如果有一个GrantedAuthority返回一个String(通过getAuthority()方法)完全等于一个或多个以ROLE_开头的ConfigAttributes,它将允许访问。 如果没与ROLE_开头的任何ConfigAttribute完全匹配,则RoleVoter将拒绝访问。如果没有ConfigAttribute以ROLE_开头,则弃权。

AuthenticatedVoter

另外一个实现是AuthenticatedVoter,它可以用来区分匿名用户,认证过的用户和remember-me认证的用户。许多站点允许在remember-me身份验证下进行某些有限访问,但需要用户通过登录进行完全访问来确认其身份。

当使用IS_AUTHENTICATED_ANONYMOUSLY属性来授予匿名访问权限的时候,AuthenticatedVoter会处理此属性。查阅此类的JavaDoc获取更多信息。

自定义Voter

显然你也可以自定义AccessDecisionVoter,并将自己的访问控制策略添加进去。它可以特定于你的应用(与业务逻辑有关),也可以实现某些安全管理逻辑。例如,例如,可以在Spring网站上找到一篇博客文章,其中介绍了如何使用Voter实时拒绝某些帐户被暂停的用户访问。

调用后处理

AbstractSecurityInterceptor在处理安全对象调用之前调用AccessDecisionManager,但某些应用需要修改安全对象调用返回的对象。可以自己很简单地通过AOP关注点来实现此功能,但SS也提供了便利的钩子,它具有几个与其ACL功能集成的具体实现。

下图描述了SS的AfterInvocationManager及其具体实现:



像SS的其他部分一样,AfterInvocationManager有一个具体的实现:AfterInvocationProviderManager,它会轮询AfterInvocationProvider列表。每个AfterInvocationProvider都可以修改返回的对象或者抛出AccessDeniedException异常。实际上,有多个provider可以修改对象,前一个provider的结果被传递到列表中的下一个provider。

注意如果使用AfterInvocationManager,还需要允许MethodSecurityInterceptorAccessDecisionManager允许某项操作的配置属性。如果使用典型的包含AccessDecisionManager实现的SS,但没有为特定的安全方法调用定义的配置属性将导致所有的AccessDecisionVoter都会投弃权票。继续往下,如果AccessDecisionManager的属性allowIfAllAbstainDecisionsfalse,则会抛出AccessDeniedException异常。可以设置allowIfAllAbstainDecisionstrue(通常不建议这么做)或者确保AccessDecisionVoter至少有一个配置属性能投票并授予权限。后者(也是推荐的方法)通过ROLE_USERROLE_AUTHENTICATED配置属性来实现。

分层角色

通常应用中的角色还应该包含其他角色。例如吗,某些应用中有“admin”和“user”的概念,“admin”可以做“user”可以做的任何事情。可以为每个“admin”分配一个“user”角色,或者修改每个需要“user”角色的访问约束为也允许“admin”角色访问。但是如果应用中包含许多不同的角色,将会变得非常复杂。

使用角色分层允许配置角色(或者权限)包含其他角色。SS的RoleVoter的扩展,RoleHierarchyVoter,配置了一个RoleHierarchy,从中可以获取用户所分配的所有“reachable authorities”。配置如下:

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
    <constructor-arg ref="roleHierarchy" />
bean>
<bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
    <property name="hierarchy">
        <value>
            ROLE_ADMIN > ROLE_STAFF
            ROLE_STAFF > ROLE_USER
            ROLE_USER > ROLE_GUEST
        value>
    property>
bean>

在这个示例中,有分层的4个角色ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST。有ROLE_ADMIN角色的用户,在使用配置了上述的RoleHierarchyVoterAccessDecisionManager进行安全约束评估的时候,就像拥有4个角色一样。>符号意思是包含。

角色分层提供了一种简便的方法来简化应用的访问控制配置数据并减少需要分配给用户的权限数量。对于更复杂的要求,可以在应用所需的特定访问权限和分配给用户的角色之间定义逻辑映射,在加载用户信息时进行转换。

安全对象实现

AOP Alliance (MethodInvocation) 安全拦截器

SS 2.0之前,安全的MethodInvocation需要大量的模板配置。现在对于方法安全的推荐方式是命名空间配置。这样,方法安全所需的基础bean就会自动配置,因此实际上不需要了解实现类。 这俩经将简要介绍涉及到的类。

方法安全使用MethodSecurityInterceptor进行,它保护MethodInvocation。根据配置的方式,拦截器可能特定于单个bean或在多个bean之间共享。拦截器使用MethodSecurityMetadataSource实例来获取适用于特定方法调用的配置属性。MapBasedMethodSecurityMetadataSource用于存储以方法名称(可以是通配符)为key配置属性,在使用元素在应用上下文中定义属性时将在内部使用。

显式MethodSecurityInterceptor配置

也可以直接在应用上下文中配置一个MethodSecurityIterceptor,以便与Spring AOP的代理机制一起使用:

<bean id="bankManagerSecurity" class=
    "org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
    <sec:method-security-metadata-source>
    <sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
    <sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
    sec:method-security-metadata-source>
property>
bean>

AspectJ (JoinPoint)安全拦截器

基于表达式访问控制

你可能感兴趣的:(Spring,Security)