I moved the authorization to mysql/sqlserver successfully (incl. salted password)!
used: spring security 3.0.3
NOTE1: add column salt (VARCHAR(25)) to your (spring security, standard db schema) users table.
NOTE2: replace spring.jar from smartgwt with spring 3.0.3 release and so on.
add
to ...-servlet.xml
add
to applicationContext.xml
add
to applicationContext-security.xml
xmlHeader for this files:
DatabasePasswordSecurerBean.java
IChangePassword.java
IPTokenBasedRememberMeServices.java
PayDBPasswordSecurerBean.java
PayJdbcDaoImpl.java
PaySaltedUser.java
b.t.w. DriverManagerDataSource is not perferct, but it works :-).
next step: migration to JNDI Datasource with tx handler.
I hope I was able to help some people.
Cheers,
Timo
used: spring security 3.0.3
NOTE1: add column salt (VARCHAR(25)) to your (spring security, standard db schema) users table.
NOTE2: replace spring.jar from smartgwt with spring 3.0.3 release and so on.
Code:
org.springframework.aop-3.0.3.RELEASE.jar org.springframework.asm-3.0.3.RELEASE.jar org.springframework.beans-3.0.3.RELEASE.jar org.springframework.context-3.0.3.RELEASE.jar org.springframework.core-3.0.3.RELEASE.jar org.springframework.expression-3.0.3.RELEASE.jar org.springframework.jdbc-3.0.3.RELEASE.jar org.springframework.orm-3.0.3.RELEASE.jar org.springframework.transaction-3.0.3.RELEASE.jar org.springframework.web-3.0.3.RELEASE.jar org.springframework.web.servlet-3.0.3.RELEASE.jar spring-security-config-3.0.3.RELEASE.jar spring-security-core-3.0.3.RELEASE.jar spring-security-web-3.0.3.RELEASE.jar
Code:
<context:annotation-config /> <context:component-scan base-package="com.package..."/>
add
Code:
<beans:bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="/WEB-INF/classes/server.properties"/>
</beans:bean>
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="${sql.mainDataBase.driver}"/>
<beans:property name="url" value="${sql.mainDataBase.driver.url}"/>
<beans:property name="username" value="${sql.mainDataBase.driver.user}"/>
<beans:property name="password" value="${sql.mainDataBase.driver.password}"/>
</beans:bean>
<beans:bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
<beans:property name="userPropertyToUse" value="salt"/>
</beans:bean>
<beans:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
<context:annotation-config />
<context:component-scan base-package="com.package..."/>
add
Code:
<beans:bean id="jdbcUserService" class="com.pay.server.authentication.PayJdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="enableGroups" value="true"/>
<beans:property name="enableAuthorities" value="false"/>
<beans:property name="usersByUsernameQuery">
<beans:value>select username, password, enabled, salt from users where username = ?</beans:value>
</beans:property>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="jdbcUserService">
<security:password-encoder ref="passwordEncoder">
<security:salt-source ref="saltSource"/>
</security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="formLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sessionAuthenticationStrategy"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationSuccessHandler">
<beans:bean class="com.pay.server.authentication.PayAuthenticationSuccessHandler"/>
</beans:property>
<beans:property name="authenticationFailureHandler">
<beans:bean class="com.pay.server.authentication.PayAuthenticationFailureHandler"/>
</beans:property>
</beans:bean>
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<beans:bean id="loggerListener" class="org.springframework.security.access.event.LoggerListener" />
xmlHeader for this files:
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"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
Code:
/**
* Secures the database by updating user passwords.
*
*/
public class DatabasePasswordSecurerBean extends JdbcDaoSupport {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SaltSource saltSource;
@Autowired
private UserDetailsService userDetailsService;
public void secureDatabase() {
getJdbcTemplate().query("select username, password from users", new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
UserDetails user = userDetailsService.loadUserByUsername(username);
String encodedPassword = passwordEncoder.encodePassword(password, saltSource.getSalt(user));
getJdbcTemplate().update("update users set password = ? where username = ?",
encodedPassword,
username);
logger.debug("Updating password for username: "+username+" to: "+encodedPassword);
}
});
}
Code:
public interface IChangePassword extends UserDetailsService {
void changePassword(String username, String password);
}
Code:
public class IPTokenBasedRememberMeServices extends
TokenBasedRememberMeServices {
private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();
public HttpServletRequest getContext() {
return requestHolder.get();
}
public void setContext(HttpServletRequest context) {
requestHolder.set(context);
}
protected String getUserIPAddress(HttpServletRequest request) {
return request.getRemoteAddr();
}
// lifecycle methods
@Override
public void onLoginSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication successfulAuthentication) {
try
{
setContext(request);
super.onLoginSuccess(request, response, successfulAuthentication);
}
finally
{
setContext(null);
}
}
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens,
HttpServletRequest request, HttpServletResponse response) {
try
{
setContext(request);
// take off the last token
String ipAddressToken = cookieTokens[cookieTokens.length-1];
if(!getUserIPAddress(request).equals(ipAddressToken))
{
throw new InvalidCookieException("Cookie IP Address did not contain a matching IP (contained '" + ipAddressToken + "')");
}
return super.processAutoLoginCookie(Arrays.copyOf(cookieTokens, cookieTokens.length-1), request, response);
}
finally
{
setContext(null);
}
}
// the worker methods
@Override
protected String makeTokenSignature(long tokenExpiryTime, String username,
String password) {
return DigestUtils.md5DigestAsHex((username + ":" + tokenExpiryTime + ":" + password + ":" + getKey() + ":" + getUserIPAddress(getContext())).getBytes());
}
@Override
protected void setCookie(String[] tokens, int maxAge,
HttpServletRequest request, HttpServletResponse response) {
// append the IP adddress to the cookie
String[] tokensWithIPAddress = Arrays.copyOf(tokens, tokens.length+1);
tokensWithIPAddress[tokensWithIPAddress.length-1] = getUserIPAddress(request);
super.setCookie(tokensWithIPAddress, maxAge, request, response);
}
Code:
public class PayDBPasswordSecurerBean extends JdbcDaoSupport {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SaltSource saltSource;
@Autowired
private UserDetailsService userDetailsService;
public void secureDatabase() {
getJdbcTemplate().query("select username, password from users", new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
UserDetails user = userDetailsService.loadUserByUsername(username);
String encodedPassword = passwordEncoder.encodePassword(password, saltSource.getSalt(user));
getJdbcTemplate().update("update users set password = ? where username = ?", encodedPassword, username);
logger.debug("Updating password for username:" + username+" to: "+encodedPassword);
}
});
}
Code:
public class PayJdbcDaoImpl extends JdbcUserDetailsManager implements IChangePassword {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SaltSource saltSource;
public void changePassword(String username, String password) {
UserDetails user = loadUserByUsername(username);
String encodedPassword = passwordEncoder.encodePassword(password, saltSource.getSalt(user));
getJdbcTemplate().update("UPDATE USERS SET PASSWORD = ? WHERE USERNAME = ?", encodedPassword, username);
}
@Override
protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) {
String returnUsername = userFromUserQuery.getUsername();
if (!isUsernameBasedPrimaryKey()) {
returnUsername = username;
}
return new PaySaltedUser(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(),
true, true, true, combinedAuthorities, ((PaySaltedUser) userFromUserQuery).getSalt());
}
@Override
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(getUsersByUsernameQuery(), new String[] {username}, new RowMapper<UserDetails>() {
public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
String salt = rs.getString(4);
return new PaySaltedUser(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES, salt);
}
});
}
Code:
public class PaySaltedUser extends User {
private static final long serialVersionUID = 896890224687744910L;
private String salt;
public PaySaltedUser(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, List<GrantedAuthority> authorities, String salt) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired,
accountNonLocked, authorities);
this.salt = salt;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
next step: migration to JNDI Datasource with tx handler.
I hope I was able to help some people.
Cheers,
Timo
Comment