在通过各种认证途径获得Authentication认证对象后,事情的发展并没有结束。用户认证只是确定当前用户是否存在,而且她的身份也被辨认出来。至于已经认证的用户是否有权限操控目标资源(web资源,业务方法,领域对象),那么就还需要经过用户授权的考验!
下面就总体上给出acegi是如何实施用户授权工作的,以及围绕web资源的授权操作展开!
当acegi完成用户的认证操作时,认证结果会存储到Authentication对象中。针对不同的场合,Acegi内置了多种Authentication对象类型。比如:PrincipalAcegiUserToken,UsernamePasswordAuthenticationToken,X509AuthenticationToken等,他们必须实现Authentication接口!
那么acegi是如何实施用户授权的呢?当已经认证的用户视图通过手中的角色集合操控目标资源时,acegi内置的AOP拦截器会采用公平投票机制评估这一举动,实际上公平投票贯穿于目标资源的调用前后,调用前,AOP拦截器确保当前用户拥有的角色集合中存在满足要求的角色(满足要求的角色,即有权限访问目标资源的角色);调用后,AOP拦截器会审核操作结果,比如从返回的领域对象集合中删除当前用户不能够操控的那些领域对象!
在实施用户授权的过程中,拦截器起到的重要的作用,比如:FilterSecurityInterceptor是一个过滤器,针对web资源;MethodSecurityInterceptor是一个AOP拦截器,针对业务方法实施授权;AspectJSecurityInterceptor拦截器,真对AspectJ方法调用实施用户授权!
这些拦截器都会分为三个阶段:事前评估,操控资源,事后审查!
事前评估:
下面的配置信息,其中accessDecisionManager属性正是完成事前评估的!
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager" /> <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" /> <property name="objectDefinitionSource"> <value> PATTERN_TYPE_APACHE_ANT /securedpage.jsp=ROLE_ADMIN /login.jsp=ROLE_ADMIN,ROLE_USER /index.jsp=ROLE_ANONYMOUS </value> </property> </bean>
上述httpRequestAccessDecisionManager的定义如下,开发者需要为AffirmativeBased配置若干投票器(AccessDecisionVoter)
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value=“false”> <property name="decisionVoters"> <list> <ref bean="roleVoter" /> </list> </property> </bean> <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
其中AffirmativeBased是AccessDecisionManager的子类,还有ConsensusBased,UnanimousBased。他们的含义和使用是有区别的!
AccessDecisionManager采用民主决策机制判断用户是否有权访问目标程序资源,它包含了多个AccessDecisionVoter。在访问决策时每个AccessDecisionVoter都拥有投票权,AccessDecisionManager统计投票结果,并按照某种决策方式根据这些投票结果决定最终是否向用户开放受限资源的访问。Acegi的投票方式有多种,每个Voter对访问权可以投赞成(ACCESS_GRANTED,取值为1),弃权(ACCESS_ABSTAIN,取值为0)或反对(ACCESS_DENIED,取值为-1)!
Acegi提供了三个具体的类去管理投票结果,他们三个的不同存在于对投票结果的统计上。ConsensusBased类根据非弃权票中赞成票多于反对票来决定是否通过。如果赞成票多于反对票就同意通过!在同意和反对票相同时或都投弃权票时可以根据设置来决定授权与否。配置如下:
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.ConsensusBased"> <property name="allowIfAllAbstainDecisions" value=“true”> <property name="allowIfEqualGrantedDeniedDecisions" value=“true”> <property name="decisionVoters"> <list> <ref bean="roleVoter" /> </list> </property> </bean>
其中属性allowIfAllAbstainDecisions,默认值是false,意味着当配置的投票器只投了弃权票时,不允许继续执行。相比之下,true意味着允许都投弃权票!
allowIfEqualGrantedDeniedDecisions,默认值为true,意味着在赞成票不为0的情况下,且赞成票和反对票相等,则允许通过!false意味着,在赞成票不为0 的情况下,赞成票和反对票相等,不允许通过!
AffirmativeBased类只要有一个或多个投票赞成就会给予通过,同样我们可以设置在都投弃权票的时候是否通过:
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions" value=“false”> <property name="decisionVoters"> <list> <ref bean="roleVoter" /> </list> </property> </bean>
UnanimousBased类会在所有的非弃权票都投赞成票时才通过,同样的设置出现在都投弃权票的时候:
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.UnanimousBased"> <property name="allowIfAllAbstainDecisions" value=“true”> <property name="decisionVoters"> <list> <ref bean="roleVoter" /> </list> </property> </bean>
Acegi提供了AccessDecisionVoter的两个实现类。对于RoleVoter,我们需要配置它的rolePrefix属性(如ROLE_),当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
针对web资源,业务方法,领域对象的用户授权,acegi内置了各种投票器!其中RoleVoter主要针对web资源和业务方法,而AclEntryVoter主要针对领域对象!怎样使用,在后面的章节将!
事后审查:
一旦用户操控到目标资源后,调用结果可能会返回一个领域对象集合,比如,getAll()方法便会返回所有的对象集合!但是并不是任意用户都能够操作到任意领域对象的,我们需要保护他们!
在实施领域对象级的访问控制时(事后审查),acegi的AfterInvocationManager充当了非常主要的角色!
<bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property> <property name="afterInvocationManager"><ref local="afterInvocationManager"/></property> <property name="objectDefinitionSource"> <value> sample.contact.ContactManager.create=ROLE_USER sample.contact.ContactManager.getAllRecipients=ROLE_USER sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ sample.contact.ContactManager.delete=ACL_CONTACT_DELETE sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN </value> </property> </bean>
AfterInvocationManager会对业务对象操作的结果进行审查,比如操作结果中是否含有当前用户无权操作的领域对象,如果有,AfterInvocationManager会从操作结果中将这一领域对象删除!具体怎么回事?后面会讲。
保护web资源:FilterSecurityInterceptor
重新认证:
一旦用户登陆到目标系统后,他便依据其本来的身份进行各种操作,但是在一些场合用户的身份可能会被动态的改变!为了将RDBMS中角色的信息的变化同步到已经登陆的用户中,必须启用alwaysReauthenticate属性,默认值是false:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="alwaysReauthenticate" value="true"></property> ....... </bean>
RoleVoter投票器:
对于RoleVoter,默认时候只会对那些前缀为ROLE_的角色进行投票处理,吐过角色信息未使用ROLE_开头,我们需要配置它的rolePrefix属性,当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" >
开发者配置的各个角色,必须存在相应的投票器对他们进行处理,否则目标Acegi应用不能正常启动!除非validateConfigAttributes属性被显式的置为false。不过建议设置为true!
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> ....... </bean>
把web资源授权信息存储在RDBMS中:
分析FilterInvocationDefinfitionSource的运行:
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> <property name="authenticationManager" ref="authenticationManager" /> <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/> <property name="objectDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /index.jsp=ROLE_ANONYMOUS,ROLE_USER /hello.htm=ROLE_ANONYMOUS,ROLE_USER /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER /**=ROLE_USER </value> </property> </bean>
其中objectDefinitionSource属性属于FilterInvocationDefinfitionSource类型!
PATTERN_TYPE_APACHE_ANT:启用了apache ant路径风格的url匹配模式(PathBasedFilterInvocationDefinfitionMap),默认的是正则表达式(RegExpBasedFilterInvocationDefinfitionMap),他们都是FilterInvocationDefinfitionSource的子类!为了通过DBMS维护web资源授权信息,必须自己提供FilterInvocationDefiniftionSource子类!
实现基于DBMS的FilterInvocationDefiniftionSource
工程目录结构以及所需的jar包如下:
1.建表:
--将web资源配置在数据库中,更加灵活 create table web_resource( web_id number not null primary key, web_url varchar2(60) not null, web_role varchar2(100) not null );
2.