我正在尝试在使用Waffle NTML进行身份验证时在Spring Security中启用角色层次结构投票,但有一些未知问题,因为继承的角色不会像预期一样出现在主体上的权限,从而阻止拦截URL中的hasRole表达式并使用授权jsp标记库.
我根据以下指南整合了华夫饼干:https://github.com/dblock/waffle/blob/master/Docs/spring/SpringSecuritySingleSignOnFilter.md
这可以在应用程序中按预期使用标准RoleVoter工作,但问题在我尝试自定义它以使用RoleHierarchyVoter时开始,我已经自己测试了(使用LDAP身份验证提供程序)并且角色层次结构完全像预期.
组合Waffle和RoleHierarchyVoter方法的配置如下:
Waffle Specfic Config
<!-- windows authentication provider -->
<bean id="waffleWindowsAuthProvider" class="waffle.windows.auth.impl.WindowsAuthProviderImpl" />
<!-- collection of security filters -->
<bean id="negotiateSecurityFilterProvider" class="waffle.servlet.spi.NegotiateSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="basicSecurityFilterProvider" class="waffle.servlet.spi.BasicSecurityFilterProvider">
<constructor-arg ref="waffleWindowsAuthProvider" />
</bean>
<bean id="waffleSecurityFilterProviderCollection" class="waffle.servlet.spi.SecurityFilterProviderCollection">
<constructor-arg>
<list>
<ref bean="negotiateSecurityFilterProvider" />
<ref bean="basicSecurityFilterProvider" />
</list>
</constructor-arg>
</bean>
<bean id="negotiateSecurityFilterEntryPoint" class="waffle.spring.NegotiateSecurityFilterEntryPoint">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
</bean>
<!-- spring security filter -->
<bean id="waffleNegotiateSecurityFilter" class="waffle.spring.NegotiateSecurityFilter">
<property name="Provider" ref="waffleSecurityFilterProviderCollection" />
<property name="AllowGuestLogin" value="false" />
<property name="PrincipalFormat" value="fqn" />
<property name="RoleFormat" value="fqn" />
<property name="GrantedAuthorityFactory" ref="simpleGrantedAuthorityFactory" />
<!-- set the default granted authority to null as we don't need to assign a default role of ROLE_USER -->
<property name="defaultGrantedAuthority"><null/></property>
</bean>
<!-- custom granted authority factory so the roles created are based on the name rather than the fqn-->
<bean id="simpleGrantedAuthorityFactory" class="xx.yy.zz.SimpleGrantedAuthorityFactory">
<constructor-arg name="prefix" value="ROLE_"/>
<constructor-arg name="convertToUpperCase" value="true"/>
</bean>
熟悉的Spring Security配置
<!-- declare the entry point ref as the waffle defined entry point -->
<sec:http use-expressions="true"
disable-url-rewriting="true"
access-decision-manager-ref="accessDecisionManager"
entry-point-ref="negotiateSecurityFilterEntryPoint" >
<sec:intercept-url pattern="/**" access="isAuthenticated()" requires-channel="any"/>
.
. access denied handlers, concurrency control, port mappings etc
.
<sec:custom-filter ref="waffleNegotiateSecurityFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
<!-- spring authentication provider -->
<sec:authentication-manager alias="authenticationProvider" />
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleHierarchyVoter" />
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler">
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
</property>
</bean>
</list>
</property>
</bean>
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_TEST_1 > ROLE_TEST_2
ROLE_TEST_2 > ROLE_TEST_3
ROLE_TEST_3 > ROLE_TEST_4
</value>
</property>
</bean>
<bean id="roleHierarchyVoter"
class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg ref="roleHierarchy"/>
</bean>
解决方法:
管理来修复我的问题,这是我的http命名空间配置中的一个遗漏,我在调试spring安全源时发现了几个小时.
问题是如何创建DefaultWebSecurityExpressionHandler.在上面的剪辑中,它在accessDecisionManager的bean定义中创建了它作为内部bean:
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler">
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
</property>
</bean>
使用此角色heirachies用于确定在处理定义为拦截URL的规则时是否应授予访问权限,例如:
<sec:intercept-url pattern="/**" access="isAuthenticated()" requires-channel="any"/>
但是如果你想使用JSP Authorize taglib检查授权,如下所示(这是在freemarker中),它将不起作用,因为不考虑roleHeirachies:
<@security.authorize access="hasRole('ROLE_TEST_1)">
<p>You have role 1</p>
</@security.authorize>
<@security.authorize access="hasRole('ROLE_TEST_4')">
<p>You have role 4</p>
</@security.authorize>
这是因为作为内部bean创建的DefaultWebSecurityExpressionHandler仅在访问决策管理器中使用,但对于taglib表达式,将创建一个新的默认bean(不使用RoleHierarchy),除非定义了安全性http命名空间表达式处理程序.
因此,为了解决我的问题,我创建了bean DefaultWebSecurityExpressionHandler并在我的WebExpressionVoter bean定义中引用它,并将其用作表达式处理程序,如下所示:
<sec:http ... >
.
. access denied handlers, concurrency control, port mappings etc
.
<sec:expression-handler ref="defaultWebSecurityExpressionHandler" />
</sec:http>
<bean id="defaultWebSecurityExpressionHandler"
class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleHierarchyVoter" />
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="defaultWebSecurityExpressionHandler"/>
</bean>
</list>
</property>
</bean>
通过http命名空间定义为拦截URL的Web安全表达式以及使用JSP Authorize taglib的表达式,进行这些更改可确保将角色Heirarchies考虑在内.