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 !!

29 thoughts on “Custom UserDetailsService example for spring 3 security”

            1. Isn’t it hibernate already configured in application? If you need something specific.. then probably you need to work on it yourself.. and if facing any problem.. then let me know that specific problem… :-)

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

  2. 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

    1. 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>

  3. 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

  4. 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

    1. Are you sure this is the correct sourcde code. It looks like this one is the same as the one from jdbc user service.

  5. 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.

  6. 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

  7. 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)”

    1. 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.

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

Want to ask any question? Or suggest anything?