Sunday, December 26, 2010

Spring Security 3 - MVC Integration: Using A Custom Authentication Manager

In this tutorial we will create a simple Spring 3 MVC application with authentication and authorization support using Spring Security 3. We will implement a custom authentication manager for our custom requirement. This tutorial is a variation of the Spring Security - MVC Integration Tutorial (Part 2). It's highly recommended that you read that first before you read further. This tutorial will focus on the differences from the previous tutorial. Most of the code are still exactly the same in both tutorials.

Note: I suggest reading the following tutorial as well which uses the latest Spring Security 3.1
Spring Security 3.1 - Implement UserDetailsService with Spring Data JPA

What is Spring Security?
Spring Security provides comprehensive security services for J2EE-based enterprise software applications. There is a particular emphasis on supporting projects built using The Spring Framework, which is the leading J2EE solution for enterprise software development. If you're not using Spring for developing enterprise applications, we warmly encourage you to take a closer look at it. Some familiarity with Spring - and in particular dependency injection principles - will help you get up to speed with Spring Security more easily.

Source: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/introduction.html#what-is-acegi-security
Let's start by showing the contents of our modified spring-security.xml file

spring-security.xml

Half of this configuration file is still the same as with our previous spring-security.xml.

Here are the notable changes:
1. The auto-config tag is now set to false.


This is set to false so that we can assign custom filters.

2. The form-login tag and its attributes are removed.


This element had been removed because we will provide a custom authentication manager.

3. The child elements of authentication-manager are removed.


The child elements had been removed because we will provide a custom authentication manager.

4. We declared a custom AuthenticationEntryPoint bean and reference that in the http tag:

5. We declared a custom-filter tag inside the http tag:

The FORM_LOGIN_FILTER is an alias among the Spring-Security filter chain. For a thorough treatment of this subject, please read Spring Security Reference for Security Namespace Configuration and Table 2.1. Standard Filter Aliases and Ordering for the actual ordering and filter classes associated for each alias.

6. Declare the authenticationFilter bean:

UsernamePasswordAuthenticationFilter is the default class for the FORM_LOGIN_FILTER alias. We're reusing this implementation, and provide our extension points in its properties.

7. Declare the required bean properties:

CustomAuthenticationManager is our custom authentication manager. SimpleUrlAuthenticationFailureHandler and SimpleUrlAuthenticationSuccessHandler are standard classes uses by the UsernamePasswordAuthenticationFilter. We declared these two beans so that we can assign a custom failure url and a default target url. Remember we have removed the form-login tag which provides a convenient way of declaring special urls. They don't exist now so must provide it.

Let's examine the contents of CustomAuthenticationManager:

CustomAuthenticationManager

CustomAuthenticationManager implements the authenticate method to provide a custom authentication process. To retrieve the users from the database, it communicates through the UserDAO. Then it compares the results retrieved from the database to the entries received from the login form. If the username and password are the same, we throw a BadCredentialsException.

Here's another filter.

If you examine closely the spring-security.xml file, I've declared another filter named BlacklistFilter:

This filter is executed before the alias FILTER_SECURITY_INTERCEPTOR. This filter will deny web access if the username is equal to 'mike'. Sorry mike :)

BlacklistFilter

Please read the comments within the class for an explanation of this filter.

Besides the files we discussed here, everything else is still the same as with the previous tutorial Spring Security - MVC Integration Tutorial (Part 2)

Our application is now finished. We managed to setup a simple Spring 3 MVC application with authentication and authorization support using Spring Security 3. In addition, we've managed to create a custom authentication manager. We've also leveraged Spring's MVC programming model via annotation.

The best way to learn further is to try the actual application.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring3-security-mvc-integration-tutorial/

You can download the project as a Maven build. Look for the spring-security-custom-auth-manager.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and Spring Security, feel free to read my other tutorials in the Tutorials section.

References:
Spring Security 3.1.0.M2 API
Spring Security Reference Documentation
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring Security 3 - MVC Integration: Using A Custom Authentication Manager ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

31 comments:

  1. Nice post. I have only 2 comments to make:

    1. The width of your blog is too small. It is difficult to read code here.
    2. It would be nice if you use @Secured annotation in your application. There aren't many sample app with this config

    ReplyDelete
    Replies
    1. in Chrome, download "stylebot" and remove, style the elements, width etc as you want it.

      Delete
  2. @Neuqino, thanks for the comment. I've just increased the content width by a 100px. This is the maximum allowed for this Blogger template. Regarding the @Secured, I'm gonna try to make a sample app using that tomorrow.

    ReplyDelete
  3. @Neuqino, your request has been granted. I've added tutorials for @Secured. Not only that I also added tutorials for Spring's native expression-based annotations.

    ReplyDelete
  4. hi krams.
    im tango that posting my request in spring forum.
    yas i found my answer like this toturial.
    your post is nice.
    you are greate.
    thanks.
    i know that i would have more question in spring security.
    i intracting with you.
    best wishes

    ReplyDelete
  5. Perfect! days ago was looking for something like this! : D

    ReplyDelete
  6. Replies
    1. VerifEyed is the world leading technology capable of determining whether digital images are original or modified (e.g., Photoshoped). VerifEyed divides the world of digital images into two groups: those having a genuineness verification (trustworthy) and the others.

      image authentication software
      image verificiation software
      image forensic software

      Delete
  7. Thanks Krams. A Really Nice Blog..

    I have few doubts...
    ------------------------------------------------
























    -------------------------------------------------
    When I try <sec:authorize i am not getting the roles and it throws exception. I am able to authenticate the user ie login and logout. my group_id (varchar2) has ROLE_ADMIN and got primary key. user_id(varchar2) has admin and got a primary key. My TBL_USERGROUP_LINK has got a primary key and two ref keys for TBL_USER and TBL_GROUP.

    Do I need to implement my own JdbcDaoImpl class to get ROLES working. I have set my group_id ie(authorityname as ROLE_ADMIN, ROLE_USER).

    Or Should I follow the steps in your solution and access Database tables..

    -----------------------------------------------
    Please let me know..

    ReplyDelete
  8. Thanks Krams. A Really Nice Blog..

    SORRY but signs are less than and grater symbols are not showing..

    I have few doubts...

    authentication-manager alias="authenticationManager"
    authentication-provider user-service-ref='userDetailsService'
    password-encoder hash="plaintext"
    authentication-provider
    authentication-manager

    beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"
    beans:property name="rolePrefix" value="ROLE_"
    beans:property name="dataSource" ref="springSecurityDataSource"

    beans:property name="usersByUsernameQuery" value="SELECT user_id as username,password,enable as enabled FROM TBL_USER WHERE user_id = ?"

    beans:property name="authoritiesByUsernameQuery" value="SELECT u.user_id as username, a.group_id as authorityname FROM TBL_USER u JOIN TBL_USERGROUP_LINK ua on u.user_id = ua.user_id JOIN TBL_GROUP a on ua.group_id = a.group_id WHERE u.user_id = ?"

    beans:bean

    ReplyDelete
  9. Why you use
    if (auth.getName().equals(auth.getCredentials()) == true) {
    ...
    }
    but not

    if (auth.getName().equals(auth.getCredentials())) {
    ...
    }

    ReplyDelete
  10. Thank you for your great work ,

    What about user details in the session registry ?

    They are not populated when I tested your sample

    your help is highly appreciated

    ReplyDelete
  11. hi all please help,
    i an mvc application i implemented opensso with ldap for authentication,it working .
    now the problem is role based access is not working,means
    when i put the





    Agent
    com.sun.identity.agents.filter.AmAgentFilter


    Agent
    /*
    REQUEST
    INCLUDE
    FORWARD
    ERROR



    i cant login when i use the springSecurityFilterChain ,so i comment that part ,now login is working ,but problem if the user know the url(*.htm) he can acess others pages.
    how to change the spring security with out any other alteration in open sso.
    another problem i'm facing is if username and password is not correct it redirecting to opensso authentication fail page,how to avoid this also.
    we are using opensso with ldap configured manually,


    please help me
    thanks in advance

    ReplyDelete
  12. the part of my xml is not showing
    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy
    the above part i commented and using
    Agent
    com.sun.identity.agents.filter.AmAgentFilter
    in filter & filter class

    ReplyDelete
  13. @ss, you can't comment out springSecurityFilterChain, you need that for this application. Since you're actually using OpenSSO for authentication, it would require a different setup. I suggest you take a look at the related configuration CAS: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/cas.html

    You might have to implement your own classes to integrate Spring Security and OpenSSO.

    Also take a look at the following comparison: http://www.liferay.com/community/forums/-/message_boards/message/4343249

    ReplyDelete
  14. I have uploaded a new tutorial using Spring Security 3.1 (see http://krams915.blogspot.com/2012/01/spring-security-31-implement_5023.html)

    ReplyDelete
  15. Hi Krams,

    Thanks for the tutorial, I am trying to get this to work with Hibernate. I can't Authenticate the user. I think it could be my DAO class. Code is below. Could you take a look and see if you can see a problem.

    Thanks

    Kris

    package com.gymsolutions.dao;

    import com.gymsolutions.form.DbUser;
    import java.util.List;
    import org.hibernate.SessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Repository;
    import java.lang.String;

    @Repository

    @SuppressWarnings("unchecked")

    public class AuthDAO {

    protected static Logger logger = Logger.getLogger("service");

    @Autowired
    private SessionFactory sessionFactory;

    public List listUser() {
    return sessionFactory.getCurrentSession().createQuery("from DbUser").list();
    }


    public DbUser searchDatabase(String username) {
    List users = sessionFactory.getCurrentSession().createQuery("from DbUser").list();
    for(DbUser dbUser:users) {
    if ( dbUser.getUsername().equals(username) == true ) {
    logger.debug("User found - UserDAOImpl");
    return dbUser;
    }
    }
    logger.debug("User does not exist - DAO");
    throw new RuntimeException("User does not exist! - DAO");

    }



    }

    ReplyDelete
  16. grantedauthority cannot be resolved to a type.

    do i need to create it?

    thanks for the tutorial and a possible answer

    ReplyDelete
    Replies
    1. Are you using the latest Spring Security jars? If yes, some classes were already removed and deprecated. If my memory serves me right, this is one of them. I think it's the parameters/arguments that has changed.

      Delete
    2. As noted in the introduction of this guide:

      Note: I suggest reading the following tutorial as well which uses the latest Spring Security 3.1
      Spring Security 3.1 - Implement UserDetailsService with Spring Data JPA

      Delete
  17. hi krams
    how can we authenticate a user in spring mvc.
    in a special application there is a sha512 password encoding +custom password encoding and that password is inserted to database.
    On user login the password has to convert to that old password encoding and validate with those in database.
    in applicationcontex-security.xml
    authentication-manager alias="authenticationManager"
    !--authentication-provider user-service-ref="userDetailsService"

    authentication-provider--
    authentication-provider ref='myAuthenticationProvider'
    authentication-manager
    beans:bean id="myAuthenticationProvider" class="aaa.CustomUserDetailsService"
    !-- beans:property name="dataSource" ref="dataSource" --
    beans:bean

    !-- jdbc-user-service authorities-by-username-query="select u.LOGINID as username,r.ROLE as authority from xxx u join yyy ur on u.USERSRNO=ur.USERSRNO join rrr r on r.ROLECD=ur.ROLECD where u.LOGINID=?" data-source-ref="dataSource" id="userDetailsService" users-by-username-query="select u.LOGINID as username,u.LOGINPASSWORD as password,u.USERSTATUS as enabled from xxx u where u.LOGINID=?"--


    the CustomUserDetailsService class implements AuthenticationProvider its getting the password we typing in password field ,i converted this password with passwordencoding ,now i need to authenticate with database and logged in.so what things i need to do extra in the customclass to validate with custompassword.

    please help me
    thanks in advance

    ReplyDelete
  18. Let me rephrase that, in the database, you've stored the hashed form of the user's password. The hashed form is a combination of sha512 and custom encoding.

    When the CustomUserDetailsService receives the plain password from the user, it needs to be converted to sha512 and custom encoding as well before you can compare it from the hashed value from the database.

    Is this what you did?

    ReplyDelete
  19. hi,krams
    thanks for reply,
    i had implemented that,i will explain what i did
    in application security file i commented
    user-service-ref under authentication-manager which call the jdbc-user-service which has that query,and write the same in a class that take the encoded password+ role +loginid and set to a bean class and my CustomUserDetailsService which implements AuthenticationProvider.
    there in authenticate method i just compare the encoded password in db with PasswordEnder(authentication.getCredentials())
    if this returns true then set the username+ password(endoed)+ role to return new UsernamePasswordAuthenticationToken(
    but i'm not sure that is correct or not please correct me if i'm wrong.
    and i don't know what is the use of supports method i just return true.

    is this authentication process is correct.

    please clarify me.

    ReplyDelete
  20. thank you so much ,please i need spring security with hibernate .

    ReplyDelete
  21. Thank you So much Sir,,, I have Learned so much from here and got solutions for many problems.....Thanks again...

    ReplyDelete
  22. Sir,
    I need to implement captcha functionality to my spring security project, so please do some help for the same..

    ReplyDelete
  23. What happens to session-management? can we leave intact



    ReplyDelete
  24. hi,

    thank you for the tutorial! I want to use the spring security remember me function, so I add

    < security:http auto-config="false" use-expressions="true" access-denied-page="/krams/auth/denied"
    entry-point-ref="authenticationEntryPoint" >
    // ....
    < security:remember-me key="appKey" token-validity-seconds="864000" user-service-ref="customUserDetailManager"/>

    < / ...>

    and implement the detailuserservice, but it does not work. any ideas?

    ReplyDelete
  25. I think about how you got so exceptional. This is truly an interesting site, loads of stuff that I can get into. One thing I only need to say is that your Blog is so impeccable!best essays

    ReplyDelete
  26. Hi,

    Please help, In secured channel https after authenticating user successfully redirect to default URL using customSuccessHandler giving role_anonymous in filter chain and flows goes to access denied.

    Thanks in advanced.

    ReplyDelete