Spring Security 3.1 Authentication using Custom Database Implementation and JSF 2.0

In the last tutorial we used default database configuration required by Spring Security and we moved out of hard coded user credentials.

But there is every possibility that their is already a existing database for user management and we cannot create new tables as required by Spring Security default behavior.

This can be achieved very easily in few steps, let’s see it in action.

First we have to create a new class which will extend JdbcDaoImpl, currently we don’t have any other functionality to place inside our custom implementation, so we will just extend the default behavior provided by JdbcDaoImpl, but in real world you can have many other functionality as part of your implementation.

CustomJDBCDaoImpl.java

package com.mumz.jsfspringsec.dao;

import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;

public class CustomJDBCDaoImpl extends JdbcDaoImpl {
	public CustomJDBCDaoImpl(){
		super();
	}
}

Second we need to update our jsfspring-sec-bean-config.xml and add the custom implementation.

jsfspring-sec-bean-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
       http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd"
	   >
       
	<beans:bean id="navigator" name="navigator" class="com.mumz.jsfspringsec.beans.Navigator" scope="session">
	</beans:bean>
	
	<beans:bean id="loginBean" name="loginBean" class="com.mumz.jsfspringsec.beans.LoginBean" scope="prototype">
		<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
		<beans:property name="rememberMeServices" ref="rememberMeServices"></beans:property>
		<beans:property name="userDetailsService" ref="customjdbcUserService"></beans:property>
	</beans:bean>
	
	<beans:bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	 	<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<beans:property name="url" value="jdbc:mysql://localhost:3306/jsf-spring-security" />
		<beans:property name="username" value="root" />
		<beans:property name="password" value="root" />
   </beans:bean>
   
   <beans:bean id="customjdbcUserService" class="com.mumz.jsfspringsec.dao.CustomJDBCDaoImpl">
   		<beans:property name="dataSource" ref="dataSource"/>
		<beans:property name="enableAuthorities" value="true"/>
		<beans:property name="usersByUsernameQuery">
			<beans:value>SELECT JSF_SPRING_SEC_USERS_USERNAME, JSF_SPRING_SEC_USERS_PASSWORD, JSF_SPRING_SEC_USERS_ENABLED FROM JSF_SPRING_SEC_USERS WHERE JSF_SPRING_SEC_USERS_USERNAME = ?</beans:value>
		</beans:property>
		<beans:property name="authoritiesByUsernameQuery">
			<beans:value>
				 select JSF_SPRING_SEC_ROLES_USERNAME,JSF_SPRING_SEC_ROLES_ROLE_NAME from JSF_SPRING_SEC_ROLES where JSF_SPRING_SEC_ROLES_USERNAME = ?
			</beans:value>
		</beans:property>
   </beans:bean>
   
	<beans:bean id="rememberMeServices"
		class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
		<beans:property name="key" value="jsfspring-sec" /> 
		<beans:property	name="userDetailsService" ref="customjdbcUserService" /> 
		<beans:property	name="alwaysRemember" value="true" /> 
		<beans:property	name="tokenValiditySeconds" value="60" /> 
	</beans:bean>
	
	<beans:bean id="rememberMeAuthenticationProvider" 
		class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
  		<beans:property name="key" value="jsfspring-sec"/>
	</beans:bean>
	
	<beans:bean id="rememberMeFilter" 
		class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
  		<beans:property name="rememberMeServices" ref="rememberMeServices"/>
  		<beans:property name="authenticationManager" ref="authenticationManager" />
	</beans:bean>
</beans:beans>

Important thing to note above:

  1. Since we are overriding the default behaviour we have to specify queries for loading username and authorities, which is what we have done for usersByUsernameQuery and authoritiesByUsernameQuery

Third step is to use this custom implementation with our authentication manager in jsfspring-sec-security-config.xml.

jsfspring-sec-security-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	   http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd">
	   
	 <sec:http auto-config="true" use-expressions="true">
		<sec:intercept-url pattern="/pages/secure/**" access="hasRole('ROLE_USER')" />
		<sec:intercept-url pattern="/pages/unsecure/**" access="permitAll"/>
		<sec:intercept-url pattern="/pages/common/**" access="permitAll"/>
		<sec:intercept-url pattern="/**" access="permitAll"/>
		<sec:form-login login-page="/pages/common/login.jsf"/>
		<sec:remember-me key="jsfspring-sec" services-ref="rememberMeServices"/>
		<sec:logout 
			invalidate-session="true" 
			delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE" 
			logout-success-url="/pages/common/login.jsf"></sec:logout>
	</sec:http>
	
	<sec:authentication-manager alias="authenticationManager">
		<sec:authentication-provider ref="rememberMeAuthenticationProvider"></sec:authentication-provider>
		<sec:authentication-provider user-service-ref="customjdbcUserService">
		</sec:authentication-provider>
	</sec:authentication-manager>
</beans:beans>

Database schema

delimiter $$

CREATE DATABASE `jsf-spring-security` /*!40100 DEFAULT CHARACTER SET utf8 */$$

CREATE TABLE `jsf_spring_sec_users` (
  `PK_JSF_SPRING_SEC_USERS` int(11) NOT NULL AUTO_INCREMENT,
  `JSF_SPRING_SEC_USERS_USERNAME` varchar(45) NOT NULL,
  `JSF_SPRING_SEC_USERS_PASSWORD` varchar(255) NOT NULL,
  `JSF_SPRING_SEC_USERS_ENABLED` varchar(5) NOT NULL,
  `JSF_SPRING_SEC_USERS_CREATED_DT` varchar(45) NOT NULL,
  `JSF_SPRING_SEC_USERS_MODIFIED_DT` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`PK_JSF_SPRING_SEC_USERS`),
  UNIQUE KEY `JSF_SPRING_SEC_USERS_USERNAME_UNIQUE` (`JSF_SPRING_SEC_USERS_USERNAME`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

CREATE TABLE `jsf_spring_sec_roles` (
  `PK_JSF_SPRING_SEC_ROLES` int(11) NOT NULL AUTO_INCREMENT,
  `JSF_SPRING_SEC_ROLES_USERNAME` varchar(45) NOT NULL,
  `JSF_SPRING_SEC_ROLES_ROLE_NAME` varchar(45) NOT NULL,
  `JSF_SPRING_SEC_ROLES_CREATED_DT` varchar(45) NOT NULL,
  `JSF_SPRING_SEC_ROLES_MODIFIED_DT` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`PK_JSF_SPRING_SEC_ROLES`),
  KEY `FK_USERNAME_JSF_SPRING_SEC_ROLES` (`JSF_SPRING_SEC_ROLES_USERNAME`),
  CONSTRAINT `FK_USERNAME_JSF_SPRING_SEC_ROLES` FOREIGN KEY (`JSF_SPRING_SEC_ROLES_USERNAME`) REFERENCES `jsf_spring_sec_users` (`JSF_SPRING_SEC_USERS_USERNAME`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

INSERT INTO `jsf_spring_sec_users` VALUES (1,'admin','admin','true','2012-07-17 10:07:41','2012-07-17 10:07:41'),(2,'guest','guest','true','2012-07-17 16:59:13','2012-07-17 16:59:13');

INSERT INTO `jsf_spring_sec_roles` VALUES (1,'admin','ROLE_USER','2012-07-17 16:57:49','2012-07-17 16:57:49'),(2,'admin','ROLE_ADMIN','2012-07-17 16:58:08','2012-07-17 16:58:08'),(3,'guest','ROLE_USER','2012-07-17 16:59:17','2012-07-17 16:59:17');

Only three steps are required for mapping our custom implementation. Enjoy!

There are 3 comments

  1. robbinwooo (@robbinwooo)

    Sir,thanks a lot for your tutorial.I followed this series one by one,but I got a NullPointerException from this tutorial while checking the remeberme check box,please advise.

    Warning: #{loginMgmtBean.login}: java.lang.NullPointerException
    javax.faces.FacesException: #{loginMgmtBean.login}: java.lang.NullPointerExcepti
    on
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionList
    enerImpl.java:118)

    1. chandu

      Sir, thanks a lot for your tutorial. I have tried custom
      database example . but it shows
      org.springframework.security.authentication.BadCredentialsException:
      Bad credentials for all input please help me..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s