Custom UserDetailsService example for spring 3 security

So far we have learned about http basic authentication, jdbc user service and xml based user service configuration examples for securing your web applications using spring security. Lets move forward in the series. In this post, I will e giving the example code for configuring custom user details service implementation and way to use it in your application.

UserDetailsService interface is used in order to lookup the username, password and GrantedAuthorities for any given user. This interface provide only one method which implementing class need to implement.

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

Here UserDetails is container for core user information. According to docs, its implementations are not used directly by Spring Security for security purposes. They simply store user information which is later encapsulated into Authentication objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored in a convenient location. A very good sample implementation can be like User class.

In our case i.e. for custom user detail service usage, AuthenticationProvider authenticates the user simply by
comparing the password submitted in a UsernamePasswordAuthenticationToken against the one loaded by
the UserDetailsService.

Example implementation

I have taking forward the code base as written in Spring 3 hibernate integration example and modified in spring 3 xml based security demo. In the application-security.xml file, I will update the configuration to use Employee dao as custom user detail service.

<!-- Defined in employee-servlet.xml -->
<bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>

<!-- Configured in application-security.xml-->
<authentication-manager alias="authenticationManager">
	<authentication-provider user-service-ref="employeeDAO"></authentication-provider>
</authentication-manager>

Complete application-security.xml file will look like this:

< ?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	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-3.0.xsd


http://www.springframework.org/schema/security


http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">

	<http auto-config="true"  use-expressions="true">
		<intercept-url pattern="/login" access="permitAll"></intercept-url>
		<intercept-url pattern="/logout" access="permitAll"></intercept-url>
		<intercept-url pattern="/accessdenied" access="permitAll"></intercept-url>
		<intercept-url pattern="/**" access="hasRole('ROLE_USER')"></intercept-url>
		<form-login login-page="/login" default-target-url="/list" authentication-failure-url="/accessdenied"></form-login>
		<logout logout-success-url="/logout"></logout>
	</http>

	<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="employeeDAO" />
	</authentication-manager>

</beans:beans>

Also, the complete employee-servlet.xml file look like this:

< ?xml  version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
        http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.howtodoinjava.controller" />

    <bean id="jspViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages"></property>
        <property name="defaultEncoding" value="UTF-8"></property>
    </bean>
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="/WEB-INF/jdbc.properties"></bean>

    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
        p:password="${jdbc.password}"></bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
           <value>
            hibernate.connection.provider_class=org.hibernate.connection.C3P0ConnectionProvider
            hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect
            hibernate.default_schema=dbo
            hibernate.show_sql=true
       		</value>
        </property>
    </bean>

    <bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
    <bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>

    <tx:annotation-driven />
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

</beans>

Now we have to update the EmployeeDaoImpl.java to implement UserDetailsService interface and override method loadUserByUsername().

EmployeeDaoImpl.java

package com.howtodoinjava.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;

import com.howtodoinjava.entity.EmployeeEntity;

@Repository
public class EmployeeDaoImpl implements EmployeeDAO, UserDetailsService  {

	@Autowired
    private SessionFactory sessionFactory;

	@Override
	public void addEmployee(EmployeeEntity employee) {
		this.sessionFactory.getCurrentSession().save(employee);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<EmployeeEntity> getAllEmployees() {
		return this.sessionFactory.getCurrentSession().createQuery("from Employee").list();
	}

	@Override
	public void deleteEmployee(Integer employeeId) {
		EmployeeEntity employee = (EmployeeEntity) sessionFactory.getCurrentSession().load(
				EmployeeEntity.class, employeeId);
        if (null != employee) {
        	this.sessionFactory.getCurrentSession().delete(employee);
        }
	}

	@SuppressWarnings("deprecation")
	@Override
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException
	{
		System.out.println("Getting access details from employee dao !!");

		// Ideally it should be fetched from database and populated instance of
		// #org.springframework.security.core.userdetails.User should be returned from this method
		UserDetails user = new User(username, "password", true, true, true, true, new GrantedAuthority[]{ new GrantedAuthorityImpl("ROLE_USER") });
		return user;
	}
}

In the above dao, I have used minimal code to show the usage of involved classes and in enterprise application, a proper access to database should be made and the user’s password and its role should be set.

The whole idea is to return the User instance with populated values inside the method. If your have other requirements, then you are free to implements UserDetails interface also and spring will not prevent you from using it.

Test the application

To test the application, simply hit the URL “http://localhost:8080/Spring3HibernateIntegration” in browser window. A login box will appear lie below:

default-login-screen-spring-security

Now login with correct username and password (i.e. lokesh and password) will let you enter into the application and employee management screen will appear. Otherwise access denied page will shown as below:

unauthorized-access-spring-security

Happy Learning !!

Lokesh

I have 7 Years of rich experience in java technology. This has only increased my hunger to learn more. In this blog, i will be writing on different topics occasionally, and would love to engage in some meaningful serious discussions with you folks.

You may also like...

29 Responses

  1. Sumit Modi says:

    I want this example in eclipse IDE,if anyone have this example please send me at sumit.modi777@gmail.com

  2. Sumit Modi says:

    I want code of this example..please send me at sumit.modi777@gmail.com

  3. Sriram says:

    Hi I have followed this and got it correct :) Thanku for the post :) Can you help me out the same for security ldap server??

  4. Eswar says:

    hi i dont want to display that access denied page instead i need to display error message in index.jsp page and i let the user to to login thereonly

    • Lokesh says:

      Spring docs suggest to use a request parameter in login url itself. They says:

      Maps to the authenticationFailureUrl property of UsernamePasswordAuthenticationFilter. Defines the URL the browser will be redirected to on login failure. Defaults to “/spring_security_login?login_error”, which will be automatically handled by the automatic login page generator, re-rendering the login page with an error message.

      So basically you have to define authentication-failure-url=”/login?error_code=1″ and use it code somewhere in your JSP like this:

      <c:if test=”${param.error_code == ’1′}”>
      <span><spring:message code=”loginPage.authenticationFailure” /></span>
      </c:if>

  5. Venkata Sriram says:

    Hi sir,I used Spring Security UserDetailService in my project and if valid user,its working fine.Iam having some doubts related to Httpsession sir.after the login through spring security,i want to set some flags after successful login ,in loadUserByUsername() again i have to make a call to DB sir.and another one is i want to make some objects into sessionScope,requestScope,is it like General way of Creating HttpSession in spring also sir or any other way is there.because iam working in cluster servers sir,session is always broken sir.Please Help me sir.Thanks

  6. Anil says:

    Thank you very much lokesh..u have explanation is great

  7. San says:

    Hi Lokesh, can u please provide the same example with only Annotations and no xml file approach??

  8. algae514 says:

    Great one !!!

    Just in case …

    I was using Spring 3.1 found that GrantedAuthorityImpl is depricated. We can use SimpleGrantedAuthority instead.

    ref below:

    http://stackoverflow.com/questions/14035953/alternative-to-grantedauthorityimpl-class

  9. Naveen says:

    Excellent lokesh :) It could help me a lot..thank you

  10. catalin says:

    thanks Lokesh for the response but as I see in the link there is the code sourse for jdbc user service. So,I have to do the above changes over this code?thx

  11. Lokesh Gupta says:

    Use this link: https://docs.google.com/file/d/0B7yo2HclmjI4UTU3OGxvMEUwZlE/edit?usp=sharing

    Don’t know how I forgot to add the download link. I will update the post.

  12. catalin says:

    Hi,
    Could you tell me please where i can find the entire source code for this (custom UserDetailService). I didn’t understood very well…this tutorial is based on Spring 3 hibernate integration example? Thanks in advance.

  13. asis says:

    hey i am looking for a example on login authentication with table have user and pass with a no of user in the table
    need to get login by the specific username and password using spring mvc+springsecurity +hibernate please do suggest me

  14. Naresh says:

    Can you post some example on the below requirement
    my application User is pre-authenticated via third party system and third party system will pass the userid as the request parameter to my application
    I want to use Spring security only for authorization (roles are stored in database)”

  15. pupil03 says:

    Can you tell me if using hibernate in spring security is better in terms of security than jdbc?? :D

    • Lokesh Gupta says:

      Well, first of all that is not the subject of post. I am saying this so other visitors of this page does not get confuse by my argument.
      My take on your question is that it neither will prove effective, if used carelessly. Its not hibernate or JDBC which makes program secure, its how you use them to secure your application.
      JDBC has very low level APIs which makes it more vulnerable if used incorrectly. Hibernate on other hand take care of some basic things, but still you need to do a lot of things extra. to make your application robust and secure.

  16. pupil03 says:

    Uhhm, can you tell me the advantages, in terms of spring security, of using hibernate rather than jdbc? :))

  17. Arjun says:

    Its better than spring docs because they lack complete samples.

Note:- In comment box, please put your code inside [java] ... [/java] OR [xml] ... [/xml] tags otherwise it may not appear/post as intended.


Want to ask any question? Or suggest anything?