We have already seen how to use Spring Security and custom database for authentication in earlier post.
But that implementation is saving password as plaintext, which is not an option at all. Both our user admin and guest are essential for application to run, in a usual scenario when an application is deployed, setting up default user and password is a generic company standard based approach.
For our application after we are done with the setup, we will encrypt password using Spring. And we will also update our security configuration to use encrypted password for authentication.
Note: All the pages remain the same, you can check this link for code related to our web pages.
First we will update our jsfspring-sec-security-config.xml
to use SHA as the password encoder.
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 hash="sha" ref="passwordEncoder"></sec:password-encoder> </sec:authentication-provider> </sec:authentication-manager> </beans:beans>
Second we will add the configuration for passwordEncoder
and we will also add a bean which will encrypt password for our default user admin and guest, for the very first time when application is run. So 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="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:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" 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='NO' || 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>
We added configuration for passwordEncoder
at line 61,62 and then we configured our bean which will encrypt password from line 64-73. Also note in our SQL statement to select users we have added JSF_SPRING_SEC_USERS_ENCRYPTED='NO' || JSF_SPRING_SEC_USERS_ENCRYPTED IS NULL
which ensures that password is encrypted only once.
Let’see how our DBPasswordEncrypterBean
bean looks like:
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.authentication.encoding.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.encodePassword(rs.getString("JSF_SPRING_SEC_USERS_PASSWORD"), null); 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; } }
We need to update our JSF_SPRING_SEC_USERS table, below is the create statement.
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, `JSF_SPRING_SEC_USERS_ENCRYPTED` 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$$
That’s all we have added encoded password for authentication and also we secured our database.
I read this paragraph fully on the topic of the resemblance of latest
and preceding technologies, it’s awesome article.
For me, this class is depracted ” import
org.springframework.security.authentication.encoding.PasswordEncoder;”
and there are some errors . I’m using 3.1.4 version of
spring-security.
I really do have a problem, This is my requirement, thought the above post would cater my requirement yet it seems not.
1. I need to use a custome login page developed in JSF 2.0, which means an .xhtml page in this scenario.
2. Wanted to save the encrypted or hashed password in the Database, for an example if the username/password is rashen/333333 then it should not save 333333 in the database, instead it should save an encrypted password, preferbly Bcrypt would do.
3. I just want to know where exactly spring will convert the normal password to an encrypted ? or is this even possible ?
4. Do we have to manually check the password? I mean dcrypt and compare or match ?