SpringSecurity参考文档PartIV.授权2.0.x作者:BenAlex,LukeTaylor译者:王耀军(2008.8.8)翻译说明2008年8月8日,今晚北京的奥运开幕式即将到来,就在这个下午我终于完成了这份文档的翻译。SpringSecurity是个好东西,最近为了新的项目花了些时间来学习,但是网上找不到一个比较好的资料,对授权部分弄不太懂,对着英文版的ReferenceDocumentation,虽然大体能看懂,但是终究理解的不够细致,因此萌发了仔细研读的想法,并籍此机会同时翻译出来,放到网上也好方便后来者。本文正好与沈哲翻译的GettingStart部分相呼应,一头一尾,中间还有PartIIOverallArchitecture和PartIIIAuthentication,不知道会不会也有机会被翻译共享出来。由于本人也是在学习,因此难免翻译会有不当之处,如果你发现了,请不吝赐教,我会更正了重新发出。如果你看了本文觉得有帮助,我也很乐意分享你的喜悦。邮件可以发到wangyaojun@gmail.com。PartIV.授权具备先进的授权处理能力,是SpringSecurity之所以如此受欢迎的原因之一。不论你选择如何进行验证——是否使用了SpringSecurity提供的机制和Provider,或与容器及其它非SpringSecurity的认证授权机制集成——你会发现可以在你的应用中以一致且简单的方式使用授权服务。本部分中,我们来研究第一部分中介绍过的AbstractSecurityInterceptor的不同实现。然后我们看看如何通过域对象的访问控制列表(ACL)来调整授权。一般授权概念22.1.权限(Authorities)正如在前面Authentication部分简单提到过的,所有Authentication的实现都必须保存一个GrantedAuthority对象数组。这代表已经授予给principal(相当于用户)的权限。AuthenticationManager把GrantedAuthority对象插入到Authentication对象中,然后由AccessDecisionManager在做授权决定的时候使用。GrantedAuthority只有一个方法接口:StringgetAuthority();AccessDecisionManager可以通过该方法获取到一个精确代表GrantedAuthority的字符串。通过返回一个代表字符串这样的方式,使得GrantedAuthority可以被大多数AccessDecisionManager“读取”到。如果GrantedAuthority不能精确地用字符串代表,这样的GrantedAuthority被认为是“集合体”,这种情况下getAuthority()必须返回null。“集合体”GrantedAuthority的一个例子如:一个储存了可以分配给不同用户的操作和授权列表的实现。用字符串来表示这样一个集合体GrantedAuthority会很复杂,因此getAuthority()方法应返回null。这告诉AccessDecisionManager应该对GrantedAuthority实现提供特别支持才能获得具体内容。SpringSecurity带有一个GrantedAuthority的实现——GrantedAuthorityImpl。该实现允许把任何用户指定的字符串转换为GrantedAuthority。所有SecuritySecurity框架的AuthenticationProvider都是用GrantedAuthorityImpl来填充Authentication对象。22.2.调用前处理(Pre-InvocationHandling)AccessDecisionManager由AbstractSecurityInterceptor调用,它负责作出最后的访问控制决定。AccessDecisionManager接口包括了如下3个方法:voiddecide(Authenticationauthentication,Objectobject,ConfigAttributeDefinitionconfig)throwsAccessDeniedException;booleansupports(ConfigAttributeattribute);booleansupports(Classclazz);从第一个方法可以看出,作出授权评估结果的信息是通过3个参数传递给AccessDecisionManager的。特别是,通过传入安全对象(注:方法中的第二个参数),使得在实际安全对象访问中的参数能被访问到。例如,我们假设安全对象是一个MethodInvocation。可以很容易的获取到MethodInvocation对象,并从中获得用户参数,然后据此在AccessDecisionManager中实现安全逻辑,以确认用户是否被允许进行此操作。如果访问时不被允许的,应该抛出一个AccessDeniedException。supports(ConfigAttribute)由AbstractSecurityInterceptor在开始的时候调用,以便确认AccessDecisionManager是否能处理传递过来的ConfigAttribute参数。supports(Class)方法由安全拦截器的实现调用以确认配置的AccessDecisionManager是否支持传递过来的安全对象类型。用户可以实现自己的AccessDecisionManager来控制授权的所有方面,SpringSecurity包含了几个基于投票策略的AccessDecisionManager的实现。图22.1,“VotingDecisionManager”给出了相关类的关系图。图22.1.VotingDecisionManager通过这种方式,一系列AccessDecisionVoter的实现被带进授权决议中。AccessDecisionManager这些投票者们的投票决定是否允许访问,如果不允许,抛出一个AccessDeniedException。AccessDecisionVoter接口也有3个类似的方法:intvote(Authenticationauthentication,Objectobject,ConfigAttributeDefinitionconfig);booleansupports(ConfigAttributeattribute);booleansupports(Classclazz);vote方法的具体实现返回一个int值,且必须是AccessDecisionVoter的3个静态变量ACCESS_ABSTAIN,ACCESS_DENIED和ACCESS_GRANTED之一。如果不能作出任何决定,应该返回ACCESS_ABSTAIN表示弃权。如果能做出决定,则必须是ACCESS_DENIED(拒绝)和ACCESS_GRANTED(授权)二者之一。SpringSecurity提供了3个基于投票策略的AccessDecisionManager实现。ConsensusBased基于授权票数与拒绝票数来决定授权与否(不计入弃权票),如果授权数大于拒绝数,则获得授权,否则决绝访问。可以通过设置属性来控制票数相等或全部弃权时的行为。AffirmativeBased的策略是,如果有一个或多个投票者投ACCESS_GRANTED,则获得授权。跟ConsensusBased相似,它也有一个参数可以控制全部投票者弃权时的行为。UnanimousBased仅在全部投ACCESS_GRANTED票的时候才能获得授权,忽略弃权票(即,只要有一个投拒绝票就拒绝)。跟其他实现类似,它也有一个参数可控制在全部弃权情况下的行为。用户可以实现自己定制的AccessDecisionManager,以不同的方式来进行计票。例如,一个特别的AccessDecisionVoter可能接受附加的权重值,或者其中有一个投票者可以一票否决。SpringSecurity提供了两个AccessDecisionVoter的实现。RoleVoter,会对任何一个以“ROLE_”开头的ConfigAttribute进行投票。如果其中有一个GrantedAuthority返回的字符串(通过getAuthority()方法)与ConfigAttributes中一个或多个以“ROLE_”开头的一致,则投授权票。如果没有一致的,则投拒绝票。如果没有一个ConfigAttribute是以“ROLE_”开头的,则投弃权票。RoleVoter在进行一致性对比的时候是大小写敏感的。BasicAclEntryVoter是另一个SpringSecurity自带的实现。它与SpringSecurity的AclManager(后面会提到)集成。该投票者被设计为在一个应用中可以有多个实例,如:beanid=aclContactReadVoterclass=org.springframework.security.vote.BasicAclEntryVoterpropertyname=processConfigAttributevalue=ACL_CONTACT_READ/propertyname=processDomainObjectClassvalue=sample.contact.Contact/propertyname=aclManagerref=aclManager/propertyname=requirePermissionlistreflocal=org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION/reflocal=org.springframework.security.acl.basic.SimpleAclEntry.READ//list/property/beanbeanid=aclContactDeleteVoterclass=org.springframework.security.vote.BasicAclEntryVoterpropertyname=processConfigAttributevalue=ACL_CONTACT_DELETE/propertyname=processDomainObjectClassvalue=sample.contact.Contact/propertyname=aclManagerref=aclManager/propertyname=requirePermissionlistreflocal=org.springframework.security.acl.basic.SimpleAclEntry.ADMINISTRATION/reflocal=org.springframework.security.acl.basic.SimpleAclEntry.DELETE//list/property/bean在上面的例子中,你要定义与MethodSecurityInterceptor或AspectJSecurityInterceptor中一些方法对应的ACL_CONTACT_READ或ACL_CONTACT_DELETE。当那些方法被调用的时候,上面定义的相关voter会进行投票。voter会从方法调用中查找第一个类型为sample.contact.Contact的参数,然后把该Contact传递给AclManager。AclManager接着会返回一个应用在当前Authentication上的访问控制列表(ACL)。假设ACL包含有需要的Permissions中的一个,voter即投授权票。如果ACL不含有任何一个v