I like finding working code so here's some as a solution (particularily for Oracle, but simple to genericize to any old database):
Authentication Provider
Code:
class DatabaseUserAuthenticationProvider implements AuthenticationProvider {
DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Authentication authenticate(Authentication authentication) {
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
Vector<GrantedAuthority> roles = new Vector<GrantedAuthority>();
logger.debug("Attempting to Authenticate " + username + ":" + password);
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement statusStmt = conn.prepareStatement("SELECT LOCK_DATE, EXPIRY_DATE, ACCOUNT_STATUS FROM DBA_USERS WHERE USERNAME = UPPER(?)");
statusStmt.setString(1, username);
ResultSet rs = statusStmt.executeQuery();
if (rs.next()) {
Object lockDate = rs.getTimestamp("LOCK_DATE");
Object expiryDate = rs.getTimestamp("EXPIRY_DATE");
String status = rs.getString("ACCOUNT_STATUS");
if (lockDate != null || expiryDate != null) {
throw new LockedException(status);
}
}
else {
throw new BadCredentialsException("Username Unknown");
}
statusStmt.close();
}
catch (SQLException e) {
throw new BadCredentialsException("Invalid credentials");
}
finally {
try {
if (conn != null)
conn.close();
}
catch(SQLException e) {
}
}
conn = null;
try {
conn = dataSource.getConnection(username, password);
PreparedStatement statusStmt = conn.prepareStatement("SELECT GRANTED_ROLE FROM USER_ROLE_PRIVS");
ResultSet rs = statusStmt.executeQuery();
while (rs.next()) {
String role = rs.getString("GRANTED_ROLE");
if (!role.startsWith("ROLE_"))
role = "ROLE_" + role;
roles.add(new GrantedAuthorityImpl(role));
}
statusStmt.close();
}
catch (SQLException e) {
throw new BadCredentialsException("Invalid credentials");
}
finally {
try {
if (conn != null)
conn.close();
}
catch(SQLException e) {
}
}
GrantedAuthority [] aRoles = new GrantedAuthority[roles.size()];
roles.toArray(aRoles);
logger.debug("Authenticated " + username + ":" + password + ":" + aRoles);
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(username, password, aRoles);
return auth;
}
public boolean supports(Class authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
What you need in your security xml configuration
Code:
<bean id="authenticationProvider"
class="DatabaseUserAuthenticationProvider">
<property name="dataSource" ref="rawDataSource"/>
<security:custom-authentication-provider/>
</bean>
Viola! If you have improvements or some such things, let me know. Or if you know how to authenticate using SYS.USER$ using the more conventional DaoAuthenticationProvider + JdbcUserDetailsManager