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

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.

Related

Spring Security and custom Remember me filter: logout issue

I need to define a custom RememberMeAuthenticationFilter so that I can override the onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) method to put some custom logic.
I've configured the XML in order to use my custom filter:
<security:http disable-url-rewriting="true" request-matcher-ref="excludeUrlRequestMatcher" entry-point-ref="authenticationEntryPoint">
<security:custom-filter position="FORM_LOGIN_FILTER" ref="usernamePasswordAuthenticationFilter"/>
<security:custom-filter position="REMEMBER_ME_FILTER" ref="extRememberMeProcessingFilter"/>
<security:anonymous username="anonymous" granted-authority="ROLE_ANONYMOUS"/>
<security:session-management session-authentication-strategy-ref="fixation" />
<!-- Intercepts url HERE: removed for brevity -->
<!--<security:form-login: using custom filter -->
<!--login-page="/login"-->
<!--authentication-failure-handler-ref="loginAuthenticationFailureHandler"-->
<!--authentication-success-handler-ref="loginGuidAuthenticationSuccessHandler"/>-->
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler"/>
<security:port-mappings>
<security:port-mapping http="#{configurationService.configuration.getProperty('tomcat.http.port')}"
https="#{configurationService.configuration.getProperty('tomcat.ssl.port')}"/>
<security:port-mapping http="80" https="443"/>
<!--security:port-mapping http="#{configurationService.configuration.getProperty('proxy.http.port')}"
https="#{configurationService.configuration.getProperty('proxy.ssl.port')}" /-->
</security:port-mappings>
<security:request-cache ref="httpSessionRequestCache"/>
<security:access-denied-handler ref="b2bAccessDeniedHandler"/>
<!-- RememberMe: using custom filter -->
<!--<security:remember-me key="comtestrememberme" services-ref="rememberMeServices"/>-->
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="myAuthenticationProvider"/>
<security:authentication-provider ref="rememberMeAuthenticationProvider"/>
</security:authentication-manager>
<bean id="myAuthenticationProvider"
class="com.test.security.MyAuthenticationProvider">
<property name="bruteForceAttackCounter" ref="bruteForceAttackCounter"/>
<property name="customerService" ref="customerService"/>
<aop:scoped-proxy/>
</bean>
<bean id="rememberMeServices"
class="com.test.security.MyRememberMeServices">
<property name="key" value="comtestrememberme"/>
<property name="cookieName" value="myRememberMe"/>
<property name="alwaysRemember" value="false"/>
<property name="customerService" ref="customerService"/>
<property name="useSecureCookie" value="false"/>
<aop:scoped-proxy/>
</bean>
<bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="comtestrememberme"/>
<aop:scoped-proxy/>
</bean>
<bean id="usernamePasswordAuthenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationSuccessHandler" ref="loginGuidAuthenticationSuccessHandler"/>
<property name="authenticationFailureHandler" ref="loginAuthenticationFailureHandler"/>
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login"/>
</bean>
<bean id="extRememberMeProcessingFilter" class="com.test.security.filters.ExtRememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
The remember me cookie is getting created and my custom filter is being used, but the problem is that the logout never happens.
When I click on the logout button it looks like I'm going through the authentication process again and the customer is logged in again.
If I revert back to the standard Spring Filters everything is working fine.
Have I missed something in the configuration?
What may be happening here is - your logout is working fine but you haven't deleted myRememberMe cookie on logout. So, when your session is getting invalidated on logout, remember me services is creating a new session by using myRememberMe cookie.
Solution: You can modify your configuration by adding delete-cookies attribute in your <security:logout> tag.
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler" delete-cookies="JSESSIONID,myRememberMe" />

Spring Security Role Hierarchy issues

I am trying to enable role hierarchy voting in Spring Security when authenticating using Waffle NTML but having some unknown issues in that the inherited roles do not appear as authorities on the principal as expected preventing hasRole expressions in both the intercept urls and using the authorize jsp taglibs.
I have been integrating waffle based on the following guide: https://github.com/dblock/waffle/blob/master/Docs/spring/SpringSecuritySingleSignOnFilter.md
This works within the application as expected on its own using the standard RoleVoter but the problem starts when I try to customise it to use the RoleHierarchyVoter which I have also tested on its own (using an LDAP Authentication Provider) and the role hierarchies work exactly as expected.
The config for the combined Waffle and RoleHierarchyVoter approach is as follows:
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>
Familiar Spring Security Config
<!-- 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>
Managed to fix my issues which was down to an omission in my http namespace configuration which I found from hours of debugging the spring security source.
The issue was how the DefaultWebSecurityExpressionHandler was created. In the snipped above it had created it as inner bean inside the bean definition of the accessDecisionManager:
<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>
With this the role heirachies are used to determine whether access should be granted when processing rules defined as intercept urls such as:
<sec:intercept-url pattern="/**" access="isAuthenticated()" requires-channel="any"/>
But if you want to check authorisation using the JSP Authorize taglib as below (this is in freemarker) it will not work as the roleHeirachies do not get taken into account:
<#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>
This is because the DefaultWebSecurityExpressionHandler created as an inner bean is only used within the access decision manager but for taglib expressions a NEW default bean will be created (which doesn't use the RoleHierarchy) unless an security http namespace expression-handler is defined.
So, to resolve my issues I created the bean DefaultWebSecurityExpressionHandler and referenced it within my WebExpressionVoter bean definition and also used it as the expression handler as follows:
<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>
Making these changes ensures the roleHeirarchies are taken into account for both Web Security Expressions defined as intercept URLs via the http namespace and also expressions using the JSP Authorize taglib.

Spring Security taglib sec:authorize with role hierarchy not working

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>

AOP MethodSecurityInterceptor not getting invoked in Spring Security

I am using Spring Security 3.0.7 and my application is deployed on JBOSS.
I am setting up org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor for my application to add restrictions on invocation of certain methods in services layer. But for some reason the interceptor is not getting called and I am able to invoke all the methods from a user with a role, ROLE_USER.
My security.xml looks like this:
<security:http auto-config='true' authentication-manager-ref="authenticationManager" use-expressions="true" request-matcher="ant" create-session="always"
entry-point-ref="authenticationEntryPoint" >
<security:intercept-url pattern="/login.jsp" access="permitAll" />
<security:intercept-url pattern="/configure/" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=Authentication Failed!" default-target-url="/landing.do"
always-use-default-target="true" />
<security:logout invalidate-session="true" delete-cookies="true" />
<security:session-management>
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/login.jsp"/>
</security:session-management>
</security:http>
<security:method-security-metadata-source id="securityMetadataSource">
<security:protect method="com.services.CreateUserService.createUser" access="ROLE_ADMIN"/>
<security:protect method="com.services.DeleteUser.deleteUser" access="ROLE_ADMIN"/>
</security:method-security-metadata-source>
<security:global-method-security authentication-manager-ref="authenticationManager" access-decision-manager-ref="accessDecisionManager"
metadata-source-ref="securityMetadataSource" pre-post-annotations="disabled" secured-annotations="disabled" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="myRoleVoter" class="com.interceptors.MyRoleVoter" />
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager" >
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="accessDecisionManager" class="com.interceptors.MyAccessDecisionManager" p:allowIfAllAbstainDecisions="false" >
<property name="decisionVoters">
<list>
<ref local="myRoleVoter"/>
</list>
</property>
</bean>
<bean id="methodSecurity" class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="securityMetadataSource" />
</bean>
The authentication part is working absolutely fine. However, my MethodSecurityInterceptor is never invoked and hence not even my AccessDecisionManager or RoleVoter.
If I add the reference of my accessDecisionManager in the first line, then my authentication layer stops working. All the requests pass to the AccessDecisionManager with the user as anonymous.
<security:http security="none" pattern="/login.jsp" />
<security:http auto-config='true' authentication-manager-ref="authenticationManager" access-decision-manager-ref="accessDecisionManager" use-expressions="true" request-matcher="ant" create-session="always"
entry-point-ref="authenticationEntryPoint" >
I know either I am missing some TINY configuration, but I am unable to find that configuration anywhere in the docs.
Possibly, you have declared your MethodSecurityInterceptor in your application root context, and expect it to work for the beans in the servlet context.
If you want global-method-security to work in the servlet context, you should declare it explicitly in the servlet xml configuration.
It's a very common mistake to declare AOP interceptors in the wrong context, so please check that your service layer beans are not being created(for example by autoscanning) in the servlet context.
I have figured out the solution. I have to define two authentication manager and one access decision manager. One authentication manager goes in my root context and would be used by the global method security to create the form login authentication mechanism.
The other authentication manager and the access decision manager goes in services context, where it would be used to create my custom method security interceptor.
applicationContext-Security.xml
<!-- The authentication manager responsible for authenticating the users -->
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager" >
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
The context file for the services.
service.security.xml
<!--we need a separate authentication manager for method security -->
<bean id="methodAuthenticationManager" class="org.springframework.security.authentication.ProviderManager" >
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
</list>
</property>
</bean>
<!--we need a separate accessDecisionManager for method security -->
<bean id="methodAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased" >
<property name="decisionVoters">
<list>
<ref local="myRoleVoter"/> <!-- the voter will decide weather methodInvocation is allowed or not -->
</list>
</property>
</bean>
<!-- The Method Security Interceptor to intercept all the calls to methods -->
<bean id="methodSecurityInterceptor" class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="methodAuthenticationManager"/>
<property name="accessDecisionManager" ref="methodAccessDecisionManager"/>
<property name="securityMetadataSource" ref="swiftSecurityMethodMetadataSource" />
</bean>
<security:method-security-metadata-source id="securityMetadataSource">
<security:protect method="fullyQualifiedMethod" access="Administrator"/>
</security:method-security-metadata-source>
<security:global-method-security authentication-manager-ref="methodAuthenticationManager" access-decision-manager-ref="methodAccessDecisionManager"
metadata-source-ref="swiftSecurityMethodMetadataSource" jsr250-annotations="disabled" secured-annotations="disabled"/>
Another usual reason why AOP MethodSecurityInterceptor is not invoked - you miss cglib or similar for byte code manipulation.
So only java.lang.reflect.Proxy used and thus it is not possible to proxy private methods! Just make #Secured methods public - and all be fine!

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