Spring Security taglib sec:authorize with role hierarchy not working - spring

I can't get sec:authorize hasRole() to work with the role hierarchy. If I have a user with role ROLE_BOSS which is the parent of ROLE_WORKER, then is false for some reason. In my service classes #PreAuthorize("hasRole('ROLE_WORKER')") does work however. I assumed they both used the same evaluator, so why doesn't the taglib work? Thanks for the help.
JSP:
<sec:authorize access="hasRole('ROLE_BOSS')">
<p>This shows up.</p>
</sec:authorize>
<sec:authorize access="hasRole('ROLE_WORKER')">
<p>This does not show up, but should.</p>
</sec:authorize>
-config.xml security:
<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
<sec:global-method-security pre-post-annotations="enabled">
<sec:expression-handler ref="expressionHandler"/>
</sec:global-method-security>
<bean id="permissionEvaluator" class="com.myapp.security.MyPermissionEvaluator">
<constructor-arg index="0">
<map key-type="java.lang.String" value-type="com.myapp.security.Permission">
<entry key="contractReadAccess" value-ref="contractReadPermission"/>
<entry key="contractWriteAccess" value-ref="contractWritePermission"/>
</map>
</constructor-arg>
</bean>
<bean id="contractReadPermission" class="com.myapp.security.ContractReadPermission"/>
<bean id="contractWritePermission" class="com.myapp.security.ContractWritePermission"/>
<sec:http use-expressions="true" access-decision-manager-ref="accessDecisionManager">
<sec:intercept-url pattern="/worker/**" access="isAuthenticated()" requires-channel="https"/>
<sec:intercept-url pattern="/boss/**" access="hasRole('ROLE_BOSS')" requires-channel="https"/>
<sec:form-login login-page="/login" authentication-failure-url="/login?login_error=1" authentication-success-handler-ref="successHandler"/>
<sec:logout logout-url="/logout" logout-success-url="/login" invalidate-session="true"/>
<sec:remember-me/>
</sec:http>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg>
<list>
<ref bean="roleVoter" />
<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>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</constructor-arg>
</bean>
<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_BOSS > ROLE_WORKER
</value>
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="myUserDetailsService"/>
</sec:authentication-manager>

For someone like me working with Java Config. This is a very simple solution here to just add the following code in your class extending WebSecurityConfigurerAdapter :
#Bean
public RoleHierarchyVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy());
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_BOSS > ROLE_WORKER");
return roleHierarchy;
}
private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
return defaultWebSecurityExpressionHandler;
}
#Override
public void init(WebSecurity web) throws Exception {
web.expressionHandler(webExpressionHandler());
super.init(web);
}

Very strange and I don't think this is correct, but it seems to work. I started digging through the Spring source code and I think I got it to work by taking the DefaultWebSecurityExpressionHandler out of the accessDecisionManager and placing it at the very top of all my security configurations. So at the top of my -config.xml I have this:
<bean id="webExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="permissionEvaluator" ref="permissionEvaluator"/>
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
And my accessDecisionManager is now:
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg>
<list>
<ref bean="roleVoter" />
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="webExpressionHandler"/>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</constructor-arg>
</bean>

Have you tried?
<%# taglib prefix='sec' uri='http://www.springframework.org/security/tags' %>
<sec:authorize ifAnyGranted='ROLE_BOSS,ROLE_WORKER'>
<h1>ROLE_BOSS and ROLE_WORKER can see this</h1><br/>
</sec:authorize>
or
<sec:authorize access="hasAnyRole('ROLE_BOSS','ROLE_WORKER')">
<h1>ROLE_BOSS and ROLE_WORKER can see this</h1><br/>
</sec:authorize>

Related

TAM Webseal + spring pre-authentication

Has anybody done spring pre-authentication with TAM Web-seal?
Can you please share the configuration details?
If webseal forward the request with the username in iv-user header, then it is relative simple to configure spring-security:
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint" access-decision-manager-ref="httpAccessDecisionManager">
<security:custom-filter ref="webSealPreAuthFilter" position="PRE_AUTH_FILTER"/>
...
</security:http>
<bean id="webSealPreAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="principalRequestHeader" value="iv-user"/>
<!-- exceptionIfHeaderMissing AND checkForPrincipalChanges needs to be enable to check that each request needs a "iv-user" header -->
<property name="checkForPrincipalChanges" value="true"/>
<property name="exceptionIfHeaderMissing" value="true"/>
</bean>
<alias name="authenticationManager" alias="org.springframework.security.authenticationManager"/>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<property name="authenticationEventPublisher">
<bean class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"/>
</property>
<constructor-arg name="providers">
<list>
<ref local="preAuthenticatedAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>
<bean id="preAuthenticatedAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
You need an userDetailsService but this is highly dependend on how your application works.

Method 'setProviders' is marked deprecated in Spring Security 3.2.7

I am using Spring Security 3.2.7 and I got this warning in my spring configuration
Multiple annotations found at this line:
- Method 'setProviders' is marked deprecated
security-config.xml:
<security:http auto-config="true">
<security:intercept-url pattern="/**" />
<security:form-login login-page="/login**"
default-target-url="/dashboard**"
authentication-failure-url="/login.xhtml?failed=true"/>
<security:logout logout-url="/logout" logout-success-url="/login.xhtml"/>
</security:http>
<bean id="userDao" class="com.tds.erp.dao.impl.UserDaoImpl"
autowire="default" />
<bean id="userDetailsService" class="com.tds.erp.services.impl.UserDetailServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" ></property>
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsService">
<!-- <security:password-encoder hash="bcrypt"/> -->
</security:authentication-provider>
</security:authentication-manager>
</beans>
Try with the following for the authentication manager
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg ref="daoAuthenticationProvider"/>
</bean>

How can I use intercept-method using its filters, not namespace?

I'm trying to add some intercept-method in my application (using spring security) without namespaces.
So here is what I did:
First, I added a filter to filter-chain-map named "methodSecurityInterceptor" as you can see:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/**"
filters="
ConcurrentSessionFilter,
securityContextPersistenceFilter,
sessionManagementFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor,
methodSecurityInterceptor,
logoutFilter" />
</security:filter-chain-map>
</bean>
Then I introduced its bean like this:
<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="MyMethodMetdataSource">
</property>
</bean>
<bean id="MyMethodMetdataSource" class="com.datx.dao.MyMethodMetdataSource">
</bean>
And I have my MyMethodMetadataSource implemented like this:
public class MyMethodMetdataSource extends AbstractMethodSecurityMetadataSource{
#Override
public Collection<ConfigAttribute> getAttributes(Method arg0, Class<?> arg1) {
String url = arg0.getName();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL2(url); //Here is my function which
//returns corresponding roles
return attributes;
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
Apparently I am not allowed to use methodSecurityInterceptor as it's not a filter!
So what should I do?
I've read this but I have no idea how to use it with one of Spring AOP's proxying mechanisms!
So... Any idea?
The example I gave you before, is pretty much as simple as you can make it without using the namespace <global-method-security> element.
Use Spring's AOP namespace with a pointcut matching the method(s) you want to protect:
<aop:config>
<aop:pointcut id='targetMethods' expression='execution(* org.springframework.security.TargetObject.*(..))'/>
<aop:advisor advice-ref='securityInterceptor' pointcut-ref='targetMethods' />
</aop:config>
and declare the security interceptor as a bean:
<bean id='target' class='org.springframework.security.TargetObject'/>
<bean id='securityInterceptor' class='org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor' autowire='byType' >
<property name='securityMetadataSource' ref="yourSecurityMetadataSource"/>
</bean>
External calls to that bean will then be routed through the security interceptor before the method is invoked.
I'd suggest you checkout the source and try running the test in a debugger to get a feel for how it works if you haven't use AOP before.
Luckily I found the answer to this question.
One cannot use filters for intercept-method. So I suggest using proxies instead.
So here is the solution:
Change back the filter chain to its normal:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/**"
filters="
ConcurrentSessionFilter,
securityContextPersistenceFilter,
sessionManagementFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor,
logoutFilter" />
</security:filter-chain-map>
See what I did there? I removed the methodSecurityInterceptor.
Then add a proxy:
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodSecurityInterceptor</value> <!-- Responsible for checking roles and accesspaths -->
</list>
</property>
<property name="beanNames">
<list>
<value>Manager2</value> <!--The Class that I want to protect its methods -->
</list>
</property>
</bean>
Of course we have to add these beans to application context too:
<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="MyMethodMetdataSource">
</property>
</bean>
<bean id="MyMethodMetdataSource" class="com.datx.dao.MyMethodMetdataSource">
</bean>
Here we go :)
Now every method in Manager2.java will be checked for every method call.

AccessDeniedException if using RoleHierarchyImpl

I am using role hierarchy in Spring Security.
<beans:bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
<beans:constructor-arg ref="roleHierarchy" />
</beans:bean>
<beans:bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<beans:property name="hierarchy">
<beans:value>
ROLE_USER > ROLE_GUEST
</beans:value>
</beans:property>
</beans:bean>
I am securing methods using protect-pointcut
<global-method-security secured-annotations="enabled" pre-post-annotations="enabled">
<protect-pointcut expression="execution(* my.package.*(..))"
access="ROLE_GUEST"/>
</global-method-security>
However, I got AccessDeniedException if I login with user that has authority ROLE_USER. I have no issue if I specified protect-pointcut with access="ROLE_GUEST,ROLE_USER".
Am I missing some steps? FYI, I am using Spring 3.0.5.
Thanks.
Don't forget to add a WebExpressionVoter to be able to also use expressions in http element:
<sec:http use-expressions="true" access-decision-manager-ref="accessDecisionManager">
<sec:intercept-url pattern="/index.html" access="hasRole('ROLE_AUTHENTICATED')" />
<sec:intercept-url pattern="/admin" access="hasRole('ROLE_SUPERVISOR')" />
...
So I end up with an accessDecisionManager containing a role hierarchy voter and a WebExpressionVoter, both using the same roleHierarchyImpl 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">
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
</property>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
<bean id="roleHierarchyVoter" 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_SUPERVISOR > ROLE_XX
ROLE_XX > ROLE_AUTHENTICATED
ROLE_AUTHENTICATED > ROLE_UNAUTHENTICATED
</value>
</property>
</bean>
(spring sec 3.1)
The nested beans are slightly wrong in jgraglia example above, and you don't need <ref bean="roleHierarchyVoter" /> because the hierarchy is handled in WebExpressionVoter. I'm doing this in Spring Security 4.0.0, but the code looks the same except you don't need use-expressions="true" because it's on by default.
I usually try and nest my beans as much as possible, so my code has no ref="" values unless required.
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg>
<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler" ref="webExpressionHandler" />
</bean>
</constructor-arg>
</bean>
<bean id="webExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/>
</bean>
<bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
ROLE_USER > ROLE_ANONYMOUS
</value>
</property>
</bean>
Have a look at bug report SEC-1163 and the comment below.
If you want basic support for role hierarchies, then use a RoleHierarchyVoter, instead of a RoleVoter.
So you need somethink like:
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleHierarchyVoter" />
<ref bean="authenticatedVoter" />
<ref bean="preAdviceVoter" />
<ref bean="mediaItemReadVoter" />
<ref bean="mediaItemWriteVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg ref="roleHierarchy"/>
</bean>

Spring 3.x configuration for multiple login pages

I'm using Spring 3.1 for authentication purpose.
My requirement:
Two different login pages. One for Customer and other for Employee.
Each after successful authentication, will be forwarded to respective successful URL.
My spring security configuration:
<sec:http pattern="/resources/**" security="none" />
<sec:http auto-config="true">
<sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER" />
<sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE" />
</sec:http>
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**"
filters="authenticationProcessingFilterForCustomer,authenticationProcessingFilterForEmployee" />
</sec:filter-chain-map>
</bean>
<bean id="authenticationProcessingFilterForCustomer"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForCustomer" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_customer" />
<property name="authenticationSuccessHandler" ref="customerSuccessHandler" />
<property name="authenticationFailureHandler" ref="customerFailureHandler" />
</bean>
<bean id="customerSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/customer/index.html" />
</bean>
<bean id="customerFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/customer.html?login_error=1" />
</bean>
<bean id="authenticationManagerForCustomer"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="customCustomerAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="customCustomerAuthenticationProvider" class="com.edu.CustomerCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="com.edu.CustomerUserDetailsService" />
</property>
</bean>
<bean id="authenticationProcessingFilterForEmployee"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManagerForEmployee" />
<property name="filterProcessesUrl" value="/j_spring_security_check_for_employee" />
<property name="authenticationSuccessHandler" ref="employeeSuccessHandler" />
<property name="authenticationFailureHandler" ref="employeeFailureHandler" />
</bean>
<bean id="employeeSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/employee/index.html" />
</bean>
<bean id="employeeFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/employee.html?login_error=1" />
</bean>
<bean id="authenticationManagerForEmployee"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="customEmployeeAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="customEmployeeAuthenticationProvider" class="com.edu.EmployeeCustomAuthenticationProvider">
<property name="userDetailsService">
<bean class="com.edu.EmployeeUserDetailsService" />
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="customCustomerAuthenticationProvider" />
<sec:authentication-provider ref="customEmployeeAuthenticationProvider" />
</sec:authentication-manager>
Both CustomAuthenticationProvider have implemented Support method as follows:
public boolean supports(Class<? extends Object> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
After launching application, while trying to authenticate, the message displayed in login pages are:
Your login attempt was not successful, try again.
Reason: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
I'm using Spring 3.1. Any help appreciated.
Thank You
I have done similar things in grails, what you need is:
extend UsernamePasswordAuthenticationToken, create two sub-class for employee and customer, say EmployeeUsernamePasswordAuthenticationToken and CustomerUsernamePasswordAuthenticationToken
extend UsernamePasswordAuthenticationFilter, to create different instance of EmployeeUsernamePasswordAuthenticationToken or CustomerUsernamePasswordAuthenticationToken based on current auth request
extend AuthenticationProvider for employee and custoner, create two class say EmployeeAuthenticationProvider and CustomerAuthenticationProvider, overwrite each class' supports method to support its target UsernamePasswordAuthenticationToken
you only need one authenticationManager, register both provide into it
only need one AuthenticationSuccessHandler, you can decide which url want to go in it
I also create a my own instance of AuthenticationEntryPoint to support multi entrypoint
Beginning from Spring 3.1 you have as many configuration as you want :
https://jira.springsource.org/browse/SEC-1171
You should point the authenticationManager ref in 'authenticationProcessingFilterForCustomer' and 'authenticationProcessingFilterForEmployee' beans to correct bean i.e. 'authenticationManager' which has providers. No need to define 'authenticationManagerForCustomer' and 'authenticationManagerForEmployee' beans.

Resources