ShaPasswordEncoder - Authentication problem
Hi,
I am taking a hands-on approach to Spring Security by trying to implement it on a website that I am creating. I have successfully managed to:
- login with the default login form
- login with my own login form (and logout too!)
- login using credentials stored in database (plaintext)
...but I have got stuck with trying to login using encryption. Here are the relevant code snippets:
spring-security.xml
Code:
<?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">
<intercept-url pattern="/login.htm" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/logout.htm" access="ROLE_ANONYMOUS" />
<intercept-url pattern="/resources/**" filters="none" />
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login.htm" default-target-url="/home.htm" authentication-failure-url="/login.htm?error=true" />
<logout logout-success-url="/logout.htm" />
</http>
<authentication-manager>
<authentication-provider>
<!--<password-encoder hash="sha"/>-->
<password-encoder base64="true" ref="passwordEncoder"/>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select USER_NAME, PASSWORD, ENABLED from apphub.users where USER_NAME=?"
authorities-by-username-query="select u.USER_NAME, ur.AUTHORITY from apphub.users u, apphub.user_roles ur where u.USER_ID = ur.USER_ID and u.USER_NAME=?"
/>
</authentication-provider>
</authentication-manager>
</beans:beans>
<app_name>-servlet.xml
Code:
<!-- Start: Datasources -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/apphub" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<!-- End: Datasources -->
<!-- Start: Password Encoder -->
<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
<bean class="com.classifieds.security.DatabaseBootstrapPasswordEncoder" init-method="secureDatabase" depends-on="dataSource">
<property name="dataSource" ref="dataSource" />
<property name="passwordEncoder" ref="passwordEncoder" />
</bean>
<!-- End: Password Encoder -->
</beans>
DatabaseBootstrapPasswordEncoder.java
Code:
package com.classifieds.security;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.authentication.encoding.PasswordEncoder;
/**
* A class, which will encode the bootstrap passwords loaded via SQL.
*
* NB: the application assumes a number of accounts will have been set up in the database
* using a SQL script. These are considered 'bootstrap' accounts and the passwords for
* these accounts will appear in the database in plain-text. The purpose of this class is
* to encode the plain-text passwords of the bootstrap accounts. The class executes an init
* method, which is invoked after the embedded-database bean is instantiated.
*
* @author Dan.Mortimer
*/
public class DatabaseBootstrapPasswordEncoder extends JdbcDaoSupport {
private PasswordEncoder passwordEncoder;
private final Log logger = LogFactory.getLog(getClass());
/**
* @param passwordEncoder the passwordEncoder to set
*/
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
/**
* The init method, which is invoked after the embedded-database bean is instantiated.
* Spring JdbcTemplate functionality is used to loop through all the users in the database
* and encode the password using the injected PasswordEncoder reference. Each password is
* updated individually.
*/
public void secureDatabase() {
getJdbcTemplate().query("select USER_NAME, PASSWORD from apphub.users",
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
String encodedPassword = passwordEncoder.encodePassword(password, null);
getJdbcTemplate().update("update apphub.users set PASSWORD = ? where USER_NAME = ?", encodedPassword, username);
logger.info("Updating password for username:" + username + " to:" + encodedPassword);
}
});
}
}
I have turned up the logging for Spring and I can see the following error:
2012-07-23 14:05:40,125 DaoAuthenticationProvider [DEBUG] Authentication failed: password does not match stored value
The problem is that the password I get directly out of the database DOES match with what gets created in the java class. So I cannot understand what is going wrong.
I'd be very grateful for some advice, please!