Custom AuthenticationProcessingFilter for spring security to perform actions on login

February 16th, 2009 | Tags: , ,

Question like this one popup on the spring security forum all the time. The question is almost always the same. The system must perform some custom action after a user logs in or out of the system. And almost always this action has to be performed on the session like setting an attribute or removing one. Sometimes user’s also want to put their own User object in the session for later use in the application. All these actions can be performed by writing a custom AuthenticationProcessingFilter and replacing the default instance on the filter chain with your implementation.

Before I show you how to write your very own filter, let me just say that you don’t necessarily need to do all this work just to put your user’s details in the session. You can instead write a custom UserDetailsService which returns an extended User object (or your own implementation of UserDetails) which contains your user details. I have written an article showing you how to do the same.

Now onto our filter. First the implementation :

public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter {

	@Override
	protected void onSuccessfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, Authentication authResult)
			throws IOException {
		super.onSuccessfulAuthentication(request, response, authResult);
		request.getSession().setAttribute("myValue", "My value is set");
	}
}

Our filter simply sets a session attribute after a user successfully logs into the system.

This filter now needs to be plugged in. If you haven’t yet read the section on adding your own filters to spring security, I seriously suggest that you do, we’re going to use the information provided there to make spring security use our filter instead of the default one. To plug this filter in the following goes into your security-applicationContext.xml :

<bean id="authenticationProcessingFilter" class="com.codercorp.security.MyAuthenticationProcessingFilter">
		<security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
		<property name="defaultTargetUrl" value="/main.html" />
		<property name="authenticationManager" ref="authenticationManager" />
</bean>

You also need to define an AuthenticationProcessingFilterEntryPoint since we’re going to break the default filter chain.

<bean id="authenticationProcessingFilterEntryPoint"
		class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
		<property name="loginFormUrl" value="/index.jsp" />
		<property name="forceHttps" value="false" />
	</bean>

And configure an AuthenticationManager as well :

<security:authentication-manager alias="authenticationManager" />

Your security-applicationContext.xml should look something like this :

<security:global-method-security />

<security:http auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
	<security:intercept-url pattern="/index.jsp" filters="none" />
	<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>

<security:authentication-provider user-service-ref="customUserDetailsService" />

<bean id="customUserDetailsService" class="com.codercorp.security.CustomUserDetailsService" />

<bean id="authenticationProcessingFilter" class="com.codercorp.security.MyAuthenticationProcessingFilter">
	<security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
	<property name="defaultTargetUrl" value="/main.html" />
	<property name="authenticationManager" ref="authenticationManager" />
</bean>

<security:authentication-manager alias="authenticationManager" />

<bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
	<property name="loginFormUrl" value="/index.jsp" />
	<property name="forceHttps" value="false" />
</bean>

Remember to switch off auto configuration of spring security by setting auto-config property of the http element to false. If you don’t you’ll get an exception :

Caused by: org.springframework.security.config.SecurityConfigurationException: Filters 'com.codercorp.security.MyAuthenticationProcessingFilter[ order=700; ]' and 'org.springframework.security.ui.webapp.AuthenticationProcessingFilter[ order=700; ]' have the same 'order' value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from <http> and avoiding the use of <http auto-config='true'>.

Your custom AuthenticationProcessingFilter is now ready for use and you can perform actions whenever a user logs in.

Share

No related posts.

  1. February 18th, 2009 at 20:04
    Reply | Quote | #1

    Thanks for that. Uou should add in the article to add entry-point-ref=”authenticationProcessingFilterEntryPoint” to the part, otherwise I couldn’t get it to work.

    • Gaurav Arora
      February 19th, 2009 at 11:04
      Reply | Quote | #2

      @mononoke:
      Thanks for that info, I forgot to copy the new config once I had changed it. :) Unless entry-point-ref is added, the example will not work.

      Gaurav

  2. dan
    February 20th, 2009 at 02:32
    Reply | Quote | #3

    Thank you for your post. Very helpful.

  3. February 27th, 2009 at 14:43
    Reply | Quote | #4

    Hi all and thanks for helpful post.
    i am little bit confused about session management.when i added request.getSession().setAttribute(“myValue”, “My value is set”); line my sessionlistener destroyed that session and creates a new one after redirecting to defaultTargetUrl.

    i read your other post which is about user tracking already and tried to mix these but no success.

    #AuthenticationProcessfilter
    super.onSuccessfulAuthentication(request, response, authResult);
    UserDetail userDetail = (UserDetail)authResult.getPrincipal();
    request.getSession().setAttribute(“USER_DETAIL”, userDetail);
    UserTracker.getInstance().addUser(userDetail);

    #HttpSessionListener
    public void sessionDestroyed(HttpSessionEvent sessionEvent) {
    String sessionId = sessionEvent.getSession().getId();
    UserTracker.getInstance().removeUser(sessionId);
    }

    #UserTracker
    public synchronized void addUser(UserDetail userDetail){
    loggedUsers.put(userDetail.getSessionId(),userDetail);
    }

    public synchronized void removeUser(String sessionId){
    loggedUsers.remove(sessionId);
    }

  4. February 27th, 2009 at 15:28
    Reply | Quote | #5

    hell yes i found it.
    that s about session fixation attack.for detailed solution http://static.springsource.org/spring-security/site/reference/html/ns-config.html#ns-entry-point-ref

    thanks.

    • Gaurav Arora
      February 27th, 2009 at 15:34
      Reply | Quote | #6

      Glad you found it, was about to say that you should use the migrateSession option to have best of both worlds. :)

  5. March 9th, 2009 at 20:35
    Reply | Quote | #7

    Thanks for your hints. They saved me a lot of time…

  6. Jussi
    March 23rd, 2009 at 16:04
    Reply | Quote | #8

    Any pointers on how to performs custom actions when the user logs out?

    Implementing a standard HttpSessionListener might do the trick in most cases (although the Spring integration is quite clumsy) but session scoped beans are causing me problems. Everything works fine when user actually clicks the logout link but if the session times out I get a BeanCreationException from the session scoped beans.

    • Gaurav Arora
      March 24th, 2009 at 18:25
      Reply | Quote | #9

      Thats a very known problem. I’d suggest you simply catch the BeanCreationException and ignore it. The only time the exception can occur is when spring is not managing your logout and that can only happen when the session times out.

  7. Jalpesh Patadia
    May 4th, 2009 at 23:35

    Hello Gaurav,

    Thanks for this article. It certainly helps to resolve one issue I’m having.

    What I now need is a way to have multiple elements in my web site. One which uses the auto-config=true (standard form based auth), and one which uses a custom element for RESTful access, with it’s own AuthEntryPoint.

    I can easily create two filter-mappings in web.xml, but I have two problems :

    a) Spring security (2.0.4) does not allow multiple http elements in your applicationContext files.
    b) Most of the beans in my application are going to be shared with both the access points in my site (form based and REST based)

    I’ve searched around, but haven’t found any examples on getting this resolved.

    Would you have any suggestions on how would I go about doing that ?

    Thanks,

    Jalpesh.

  8. SDB
    June 1st, 2009 at 23:06

    Hello Gaurav,

    Thanks for the post.
    I have a requirement. After successful login the user is taken to the welcome page. From there he can click on a link to navigate to another page. In the second page, I need to display some details regarding the user logged in. So my requirement is, when the user clicks the link in Welcome page, it should be taken to the controller where there will be come calculations and store the value in a session variable. When the page is rendered, then the value is to be displayed .So my doubt is how we can access the session variable in Controller java classes. Or is there any other way to attain this functionality.

    Please give me some pointers. I am new to spring. My application is using struts 2, spring, spring security and hibernate

    Thanks,
    SDB

    • Gaurav Arora
      June 2nd, 2009 at 19:20

      You can access the session from the request..

  9. Dario
    June 12th, 2009 at 01:13

    Hi! thanks for this very usefull tutorial. Anyway reading it, when you say

    “you don’t necessarily need to do all this work just to put your user’s details in the session. You can instead write a custom UserDetailsService which returns an extended User object (or your own implementation of UserDetails) which contains your user details.”

    I followed the article and I implemented my UserDetailsService, but now How do I get user details from the session? I mean, in a JSP page how do I get for example the user email? because for the username I can do

    But because the object “Principal” has a method called .getName()
    How can I do a similar thing with other user details?

    Many thanks in advance for the answer
    Dario

  10. Dario
    June 12th, 2009 at 01:16

    the previus post didn’t show the following line
    ” ”

    after “because for the username I can do ”
    And before “But because the object ”
    Sorry for the mess.. :)

  11. sunny
    August 13th, 2009 at 22:44

    I have customized AuthenticationProcessingFilter for custom authentication. All working fine except, upon the unsuccess authentication, I like to display a custom message. So I am throwing BadCredentialExecption with the custom message. Upong the unsucessfull attempt, it never takes to the login page and never displays custom message. I see all the stacktrace on the page. URL shows j_spring_security_check.

    Any idea pls? I appreciate it

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest request,
    HttpServletResponse response, AuthenticationException failed) throws AuthenticationException, IOException
    {
    throw new BadCredentialsException(“Log In information not valid: Please enter a valid Username and associated Password.”);
    }

    • Gaurav Arora
      August 18th, 2009 at 10:58

      Read this link on an idea of how to handle exceptions globally. This is possibly the easiest way to do it.

  12. Fuz
    September 3rd, 2009 at 00:34

    Great stuff!
    everything almost works! but look at this:

    here you wrote filters=”none”. if you replace this with access=”IS_AUTHENTICATED_ANONYMOUSLY” browser goes to unstoppable redirecting. do you know what is it?
    Also spring security error handling dissapeared – server HTTP Status 401 error page now appears.

    • Andi
      September 21st, 2009 at 15:46

      Hi!
      I have the same problem. It works only for filters=”none”, but then the redirect to https (reqires-channel=”https”) doesn’t work… Any idea?

  13. NaiveGeek
    April 2nd, 2010 at 03:21

    Hey Gurav,
    Thanks for the info posted. It explained lot clearly and helped me successfully accomplish my task
    Naive Geek

  14. October 28th, 2011 at 06:50

    Nice read, I just passed this onto a colleague who was doing some research on that. And he actually bought me lunch as I found it for him smile Therefore let me rephrase that: Thanks for lunch! “For most of history, Anonymous was a woman.” by Virginia Woolf.

    • Gaurav Arora
      October 28th, 2011 at 16:00

      Glad to be of help. Wish I was still working with Spring and could help more.

  15. Norman
    October 28th, 2011 at 20:02