Spring Security 3.1 Adding Salt to Password using StandardPasswordEncoder and JSF 2.0

In the last couple of post

  1. Spring Security 3.1 Adding Salt to Password using Custom Database and JSF 2.0
  2. Spring Security 3.1 Group-Based Access Control with Custom Database and JSF 2.0

We added salt to our password and implemented Group-Based Access Control. But our salt was dependent on the username, which is not a very good thing to do. Instead it would be better if some random salt is added. Thankfully Spring Security 3.1 addresses this concern with PasswordEncoder, in this tutorial we will use StandardPasswordEncoder for encoding our password. This also reduces the amount of code that needs to be written.

First we will update our jsfspring-sec-security-config.xml, we no longer require the saltSource.

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:password-encoder ref="passwordEncoder">
			</sec:password-encoder>
		</sec:authentication-provider>
	</sec:authentication-manager>
</beans:beans>

Second databasePasswordEncrypter, once again there is no need for saltSource and hence userDetailService to be injected in databasePasswordEncrypter, let’s update our jsfspring-sec-bean-config.xml.

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="false"/>
		<beans:property name="enableGroups" value="true"></beans:property>
		<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:property name="groupAuthoritiesByUsernameQuery">
			<beans:value>
				SELECT 
						GROUPDTLS.JSF_SPRING_SEC_GROUPS_GROUP_ID, 
						GROUPDTLS.JSF_SPRING_SEC_GROUPS_GROUP_NAME, 
						GROUPPERMISSION.JSF_SPRING_SEC_GROUP_AUTHORITIES_AUTHORITY 
				FROM 
						JSF_SPRING_SEC_GROUPS GROUPDTLS, 
						JSF_SPRING_SEC_GROUP_AUTHORITIES GROUPPERMISSION, 
						JSF_SPRING_SEC_GROUP_MEMBERS GROUPMEMBERS, 
						JSF_SPRING_SEC_USERS USERS 
				WHERE 
						USERS.JSF_SPRING_SEC_USERS_USERNAME = ? AND 
						GROUPMEMBERS.JSF_SPRING_SEC_GROUP_MEMBERS_USER_ID = USERS.PK_JSF_SPRING_SEC_USERS AND 
						GROUPMEMBERS.JSF_SPRING_SEC_GROUP_MEMBERS_GROUP_ID = GROUPDTLS.JSF_SPRING_GROUPS_GROUP_ID AND 
						GROUPPERMISSION.JSF_SPRING_SEC_GROUP_AUTHORITIES_GROUP_ID = GROUPDTLS.JSF_SPRING_GROUPS_GROUP_ID 
			</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:bean class="org.springframework.security.crypto.password.StandardPasswordEncoder" id="passwordEncoder">
	</beans:bean>
	
	<beans:bean id="databasePasswordEncrypter" class="com.mumz.jsfspringsec.dao.security.DBPasswordEncrypterBean" init-method="encryptDBPassword" depends-on="dataSource">
		<beans:property name="passwordEncoder" ref="passwordEncoder"></beans:property>
		<beans:property name="dataSource" ref="dataSource"></beans:property>
		<beans:property name="selectQuery">
			<beans:value>SELECT JSF_SPRING_SEC_USERS_USERNAME, JSF_SPRING_SEC_USERS_PASSWORD, JSF_SPRING_SEC_USERS_ENCRYPTED FROM JSF_SPRING_SEC_USERS WHERE (JSF_SPRING_SEC_USERS_ENCRYPTED='' || JSF_SPRING_SEC_USERS_ENCRYPTED IS NULL)</beans:value>
		</beans:property>
		<beans:property name="updateQuery">
			<beans:value>UPDATE JSF_SPRING_SEC_USERS SET JSF_SPRING_SEC_USERS_PASSWORD = ?, JSF_SPRING_SEC_USERS_ENCRYPTED='YES' WHERE JSF_SPRING_SEC_USERS_USERNAME = ? </beans:value>
		</beans:property>
	</beans:bean>
</beans:beans>

Third our DBPasswordEncrypterBean so that when our application starts, our plaintext password will be hashed with salt.
DBPasswordEncrypterBean

package com.mumz.jsfspringsec.dao.security;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * The Class DBPasswordEncrypterBean.
 * @author prabhat.jha
 */
public class DBPasswordEncrypterBean extends JdbcDaoSupport{
	
	/** The Constant selectQuery. */
	private String selectQuery = null;
	
	/** The Constant updateQuery. */
	private String updateQuery = null;
	
	/** The password encoder. */
	private PasswordEncoder passwordEncoder = null;

	/**
	 * Encrypt db password.
	 */
	public void encryptDBPassword(){
		getJdbcTemplate().query(getSelectQuery(), new RowCallbackHandler() {
			@Override
			public void processRow(ResultSet rs) throws SQLException {
				final String username = rs.getString("JSF_SPRING_SEC_USERS_USERNAME");
				final String encryptedPassword = passwordEncoder.encode(rs.getString("JSF_SPRING_SEC_USERS_PASSWORD"));
				getJdbcTemplate().update(new PreparedStatementCreator() {
					@Override
					public PreparedStatement createPreparedStatement(Connection con)
							throws SQLException {
						PreparedStatement preparedStatement = con.prepareStatement(getUpdateQuery());
						preparedStatement.setString(1, encryptedPassword);
						preparedStatement.setString(2, username);
						return preparedStatement;
					}
				});
			}
		});
	}
	
	/**
	 * Gets the password encoder.
	 * 
	 * @return the password encoder
	 */
	public PasswordEncoder getPasswordEncoder() {
		return passwordEncoder;
	}

	/**
	 * Sets the password encoder.
	 * 
	 * @param passwordEncoder
	 *            the new password encoder
	 */
	public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
		this.passwordEncoder = passwordEncoder;
	}

	/**
	 * Gets the select query.
	 * 
	 * @return the select query
	 */
	public String getSelectQuery() {
		return selectQuery;
	}

	/**
	 * Sets the select query.
	 * 
	 * @param selectQuery
	 *            the new select query
	 */
	public void setSelectQuery(String selectQuery) {
		this.selectQuery = selectQuery;
	}

	/**
	 * Gets the update query.
	 * 
	 * @return the update query
	 */
	public String getUpdateQuery() {
		return updateQuery;
	}

	/**
	 * Sets the update query.
	 * 
	 * @param updateQuery
	 *            the new update query
	 */
	public void setUpdateQuery(String updateQuery) {
		this.updateQuery = updateQuery;
	}
}

That’s all, remember to clean your database before re-running this example.

There is one comment

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