AOP MethodSecurityInterceptor not getting invoked in Spring Security - spring

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!

Related

Spring facebook login: signup redirect

SCENARIO 1
When logging in with facebook to my webapp
If the browser is already logged in to Facebook, and
if the user's social authentication details are already registered in my webapp,
then when the user clicks "Sign in with Facebook" on my webapp,
he/she is authorized by Facebook and
the Authorization object returned by
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
is the named user.
All is good.
SCENARIO 2
If the browser is NOT logged into Facebook but
the user's social authentication details are already registered in my webapp,
then when the user clicks "Sign in with Facebook" on my webapp,
the user signs in with Facebook by entering username and password BUT
the the Authorization object returned by
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
is authorized as anonymous and has the name anonymousUser.
Moreover, the call from Facebook after authorization is to /signup
However, I am expecting the same registered user as in scenario 1
My security configuration is below. Can anyone give me a hint as to what the problem may be?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http use-expressions="true" entry-point-ref="appAuthenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll()" />
<security:intercept-url pattern="/flow-entry.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata.html" access="permitAll()"/>
<security:intercept-url pattern="/flow-jobpostdata_anydegree.html" access="permitAll()"/>
<security:intercept-url pattern="/j_spring_security_check" access="permitAll()"/>
<!-- Adds social authentication filter to the Spring Security filter chain. -->
<security:custom-filter before="PRE_AUTH_FILTER" ref="socialAuthenticationFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="SecurityAuthFilter"/>
</security:http>
<!-- authentication manager and its provider( social provider deals with social login & local user provider deals with form login ) -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="socialAuthenticationProvider"/>
<security:authentication-provider user-service-ref="localUserDetailService"/>
</security:authentication-manager>
<bean id="customAuthenticationProvider" class="com.ikoda.service.loginservices.CustomAuthenticationProvider">
<property name="auService" ref="auService" />
</bean>
<bean id="socialAuthenticationProvider" class="org.springframework.social.security.SocialAuthenticationProvider">
<constructor-arg ref="inMemoryUsersConnectionRepository"/>
<constructor-arg ref="socialUserDetailService"/>
</bean>
<!-- form login beans -->
<bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</bean>
<bean id="appAuthenticationEntryPoint"
class="com.ikoda.service.loginservices.AppAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/login"/>
</bean>
<bean id="rememberMeServices"
class="org.springframework.security.web.authentication.NullRememberMeServices"/>
<bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<constructor-arg name="defaultFailureUrl" value="/login?error=true"/>
</bean>
<bean class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
id="SecurityAuthFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
<property name="authenticationFailureHandler" ref="failureHandler"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
<!-- social login filter which is a pre authentication filter and works for /auth service url -->
<bean id="socialAuthenticationFilter" class="org.springframework.social.security.SocialAuthenticationFilter">
<constructor-arg name="authManager" ref="authenticationManager"/>
<constructor-arg name="userIdSource" ref="userIdSource"/>
<constructor-arg name="usersConnectionRepository" ref="inMemoryUsersConnectionRepository"/>
<constructor-arg name="authServiceLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="authenticationSuccessHandler" ref="successHandler"/>
</bean>
<!-- inmemory connection repository which holds connection repository per local user -->
<bean id="inMemoryUsersConnectionRepository"
class="org.springframework.social.connect.mem.InMemoryUsersConnectionRepository">
<constructor-arg name="connectionFactoryLocator" ref="appSocialAuthenticationServiceRegistry"/>
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
<!-- service registry will holds connection factory of each social provider-->
<bean id="appSocialAuthenticationServiceRegistry"
class="com.ikoda.service.loginservices.AppSocialAuthenticationServiceRegistry">
<constructor-arg>
<list>
<ref bean="facebookAuthenticationService"/>
<ref bean="googleAuthenticationService"/>
</list>
</constructor-arg>
</bean>
<bean id="facebookAuthenticationService"
class="org.springframework.social.facebook.security.FacebookAuthenticationService">
<constructor-arg name="apiKey" value="11111"/>
<constructor-arg name="appSecret" value="11111"/>
</bean>
<bean id="googleAuthenticationService"
class="org.springframework.social.google.security.GoogleAuthenticationService">
<constructor-arg name="apiKey" value="111-lpmhcmuj1577bd6god0696g4u2g16c1i.apps.googleusercontent.com"/>
<constructor-arg name="appSecret" value="111-111-"/>
</bean>
<bean id="userIdSource" class="org.springframework.social.security.AuthenticationNameUserIdSource"/>
<!-- If no local user is associated to a social connection then connection sign up will create a new local user and map it to social user -->
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
</beans>
The redirect to signUp is the intended behaviour of Spring Social. The key is how to manage the behaviour.
One way to solve the problem is by authenticating the social login in the controller signup method. To do this requires accessing the social connection. You can do this by autowiring ProviderSignInUtils in your controller.
Note AppConnectionSignUP is some implementation of org.springframework.social.connect.ConnectionSignUp
Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
if (null != connection)
{
String userIdString = appConnectionSignUp.execute(connection);
}
Another way to solve the problem is to implement your own ConnectionSignUp
<bean id="connectionSignUp" class="com.ikoda.service.loginservices.AppConnectionSignUp"/>
and reference that in your UsersConnectionRepository
<bean id="jdbcUsersConnectionRepository"
class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
<constructor-arg ref="dataSource" />
<constructor-arg ref="appSocialAuthenticationServiceRegistry" />
<constructor-arg ref="textEncryptor" />
<property name="connectionSignUp" ref="connectionSignUp"/>
</bean>
If you take this approach, then social login will bypass the Controller /signUp and directly invoke the execute method of connectionSignUP bean.
Meanwhile, in the question above the inMemoryUsersConnectionRepository was ill-advised. I switched to the jdbcUsersConnectionRepository.

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.

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.

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