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