spring security 3 custom authentication manager

applicationContext.xml

<?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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	   
	<!-- for using property file -->
	 <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
          p:location="/projectfields.properties" />


	<bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource" destroy-method="close">
        <property name="driverClassName">
            <value>foo.fooserver.jdbc.FOODriver</value>
        </property>
        <property name="url">
            <value>jdbc:url</value>            
        </property>
        <property name="username">
            <value>username</value>            
        </property>
        <property name="password">
            <value>password</value>            
        </property>
        <property name="maxActive" value="100"/>
        <property name="maxWait" value="10000"/>
        <property name="maxIdle" value="10"/>
    </bean>

    <bean class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" id="sessionFactory" primary="true" >
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
		<!--packagesToScan scans all the classes for hibernate(models)-->
        <property name="packagesToScan" value="test.vipul.**.model"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="current_session_context_class">thread</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.connection.autocommit">true</prop>                
                <prop key="hibernate.max_fetch_depth">5</prop>
                <prop key="hibernate.default_batch_fetch_size">16</prop>
                <prop key="hibernate.jdbc.batch_size">25</prop>
                <prop key="hibernate.jdbc.fetch_size">8</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.connection.release_mode">after_statement</prop>
            </props>
        </property>
    </bean>
    
   
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"   p:sessionFactory-ref="sessionFactory" />
        
    <bean  class="org.springframework.orm.hibernate3.HibernateTemplate" id="hibernateTemplate">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
		
	<!-- This is where we configure Spring-Security  -->	
    <security:http auto-config="false" entry-point-ref="authenticationEntryPoint">


        <security:intercept-url pattern="/school/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <security:intercept-url pattern="/common/**" access="ROLE_TEACHER,ROLE_CLERK,ROLE_STUDENT"/>
        
        
        <security:intercept-url pattern="/clerk/**" access="ROLE_CLERK" />
        <security:intercept-url pattern="/teacher/**" access="ROLE_TEACHER"  />
        <security:intercept-url pattern="/student/**" access="ROLE_STUDENT"  />
                
        <security:logout logout-success-url="/logout" />

        <security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
        <security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
        <security:custom-filter after="CONCURRENT_SESSION_FILTER" ref="secureFilter" />
        <security:session-management session-authentication-strategy-ref="sas"/>
            
    </security:http>


	<!-- Custom filter for username and password. The real customization is done in the customAthenticationManager -->
    <bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
  		p:authenticationManager-ref="customAuthenticationManager"
  		p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
  		p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" 
                p:sessionAuthenticationStrategy-ref="sas"/>
				
    <!-- Custom authentication manager. In order to authenticate username and password. -->            
    <bean id="customAuthenticationManager" class="com.vipul.CustomAuthenticationFilter">
        <constructor-arg type="org.hibernate.SessionFactory" ref="sessionFactory"/>
		
   <!--Define maximum number of failed login attempt i.e. after how many consecutive failed attempts user's account should be disabled-->
        <constructor-arg type="int" value="${user_failedattempt}"></constructor-arg>
    </bean>
    
	<!-- Set authentication failure url here -->	
    <bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
	p:defaultFailureUrl="/loginfailed"/>
    
	<!-- Set authentication success url here -->	
    <bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
 		p:defaultTargetUrl="/submitLogin" />
   
   <!-- Filter required by concurrent session handling package 
		The ConcurrentSessionFilter requires two properties, sessionRegistry, which generally points to an 
		instance of SessionRegistryImpl, and expiredUrl, which points to the page to display when a session has expired. -->
    <bean id="concurrencyFilter"
      class="org.springframework.security.web.session.ConcurrentSessionFilter" p:expiredUrl="/sessionexpired" />
        <property name="sessionRegistry" ref="sessionRegistry" />        
    </bean>


	<!--  Defines a concrete concurrent control strategy 
		  Checks whether the user should be allowed to proceed, by comparing the number of 
		  sessions they already have active with the configured maximumSessions value. The SessionRegistry 
		  is used as the source of data on authenticated users and session data. -->
    <bean id="sas"
      class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"
        p:maximumSessions="${max_Sessions}" p:exceptionIfMaximumExceeded="true"> 
        <constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
    </bean>
    
	<!-- Maintains a registry of SessionInformation instances -->
    <bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
    
	<!-- The AuthenticationEntryPoint is responsible for redirecting the user to a particular page, like a login page,
 			whenever the server sends back a response requiring authentication -->
    <bean id="authenticationEntryPoint"  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
	 	p:loginFormUrl="/login"/>
       
	<!-- The tag below has no use but Spring Security needs it to autowire the parent property of 
			org.springframework.security.authentication.ProviderManager. Otherwise we get an error 
			A probable bug.-->
    <security:authentication-manager/>
</beans>

CustomAuthenticationFilter

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

/**
 *
 * @author Vipul.Paralikar JavaHunter
 */
public class CustomAuthenticationFilter implements AuthenticationManager {

    private SessionFactory sessionFactory;
    private int failedloginattempt;

    public CustomAuthenticationFilter(SessionFactory sessionFactory, int failedattempt) {
        this.sessionFactory = sessionFactory;
        this.failedloginattempt = failedloginattempt;
    }

    @Override
    public Authentication authenticate(Authentication a) throws AuthenticationException {
        Session session = sessionFactory.openSession();
        String loginId = null;
        String password = null;
        String userType = null;
        int failedAttempts = 0;
        
        StringBuilder hqlQuery = new StringBuilder();
        hqlQuery.append("select new map(lm.loginName as loginName,lm.loginPassword as passwrd,utm.userType as userType,lm.failedAttempt as failedAttempt) ");
        hqlQuery.append("  from TblLoginMaster lm, TblUserTypeMaster utm ");
        hqlQuery.append("  where lm.userTypeId=utm.userTypeId and lm.loginName=:loginName");
        
		// Retrieve user details from database
        Query query = session.createQuery(hqlQuery.toString());        
        query.setString("loginName", a.getName());

        List resultSetList = query.list();
        Iterator it = resultSetList.iterator();
		
		// Here's the main logic of this custom authentication manager
        if (resultSetList != null && !resultSetList.isEmpty()) {
            while (it.hasNext()) {
                Map resultMap = (HashMap) it.next();

                loginId = resultMap.get("loginName").toString();
                password = resultMap.get("passwrd").toString();
                userType = resultMap.get("userType").toString();
                failedAttempts = Integer.parseInt(resultMap.get("failedAttempt").toString());
            }
            
            if (loginId.equals(a.getName())) {
                if (failedAttempts == failedloginattempt) {
                    throw new DisabledException("User is disabled");
                } else {
                    if (password.equals(a.getCredentials().toString())) {
                        return new UsernamePasswordAuthenticationToken(a.getPrincipal(), a.getCredentials(), getAuthorities(userType));
                    } else {
                        throw new BadCredentialsException("Bad credentials");
                    }
                }
            } else {
                throw new BadCredentialsException("Bad credentials");
            }

        } else {
            throw new BadCredentialsException("Bad credentials");
        }
    }

	/**
	 * Retrieves the correct ROLE type depending on the access level, where access level is an String.
	 * 
	 * @param access an string value representing the access of the user
	 * @return collection of granted authorities
	 */
    public Collection<GrantedAuthority> getAuthorities(String access) {
        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();

        if (access.equals("TEACHER")) {
            authList.add(new GrantedAuthorityImpl("ROLE_TEACHER"));
        }
        if (access.equals("CLERK")) {
            authList.add(new GrantedAuthorityImpl("ROLE_CLERK"));
        }
        if (access.equals("STUDENT")) {
            authList.add(new GrantedAuthorityImpl("ROLE_STUDENT"));
        }
        return authList;
    }

}

projectfields.properties

max_Sessions=1
user_failedattempt=3
Advertisements
This entry was posted in Hibernate, Spring and tagged , , . Bookmark the permalink.

2 Responses to spring security 3 custom authentication manager

  1. Nirav Shah says:

    hi i implemented your code but in the customAuthenticationManager i get name and credentials as empty

    • vipulparalikar says:

      The name of form fields for username and password must match the spring security standard names i.e. j_username and j_password.

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 )

Google+ photo

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

Connecting to %s