Skip to content

Instantly share code, notes, and snippets.

@nmfisher
Created July 1, 2015 00:24
Show Gist options
  • Save nmfisher/99da9741c440cd460129 to your computer and use it in GitHub Desktop.
Save nmfisher/99da9741c440cd460129 to your computer and use it in GitHub Desktop.
Custom AuthenticationHandler for CAS using Bcrypt (standard password encoding algorithm used by Grails spring security plugin)
package com.clearframe.cas;
import java.security.GeneralSecurityException;
import java.util.Map;
import javax.security.auth.login.FailedLoginException;
import javax.validation.constraints.NotNull;
import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.UsernamePasswordCredential;
import org.jasig.cas.authentication.HandlerResult;
import org.jasig.cas.authentication.PreventedException;
import org.jasig.cas.authentication.principal.SimplePrincipal;
import org.springframework.dao.DataAccessException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.beans.factory.InitializingBean;
/**
*
* @author Nick Fisher
*/
public class BCryptSearchModeSearchDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler
implements InitializingBean {
@NotNull
private String fieldUser;
@NotNull
private String fieldPassword;
@NotNull
private String tableUsers;
private String sql;
/**
*
*/
@Override
protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
throws GeneralSecurityException, PreventedException {
final String username = credential.getUsername();
final String password = credential.getPassword();
final Map<String, Object> userDetails;
try {
userDetails = getJdbcTemplate().queryForMap(this.sql, username);
} catch (final DataAccessException e) {
throw new PreventedException("SQL exception while executing query for " + username, e);
}
String encryptedPassword = (String) userDetails.get("password");
if(!isPasswordValid(password, encryptedPassword)) {
throw new FailedLoginException("incorrect password specified for username " + username);
}
return createHandlerResult(credential, new SimplePrincipal(username), null);
}
/**
*
* @throws java.lang.Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
this.sql = "Select " + fieldUser + "," + fieldPassword + " from " + this.tableUsers + " WHERE " + this.fieldUser + " = ?";
}
/**
*
*/
private boolean isPasswordValid(String plainTextPassword, String encryptedPassword) {
if(plainTextPassword == null || plainTextPassword.trim().length() == 0 ||
encryptedPassword == null || encryptedPassword.trim().length() == 0) {
return false;
}
return BCrypt.checkpw(plainTextPassword, encryptedPassword);
}
/**
* @param fieldPassword The fieldPassword to set.
*/
public final void setFieldPassword(final String fieldPassword) {
this.fieldPassword = fieldPassword;
}
/**
* @param fieldUser The fieldUser to set.
*/
public final void setFieldUser(final String fieldUser) {
this.fieldUser = fieldUser;
}
/**
* @param tableUsers The tableUsers to set.
*/
public final void setTableUsers(final String tableUsers) {
this.tableUsers = tableUsers;
}
}
@nmfisher
Copy link
Author

nmfisher commented Jul 1, 2015

For anyone wanting to use a CAS server to authenticate against a user database which uses bcrypt for password hashing (e.g. a Grails project using the Spring Security plugin with the default password encoder), you can register a bean in deployerConfigContext.xml as follows:

<bean id="dbAuthHandler"
        class="com.clearframe.cas.BCryptSearchModeSearchDatabaseAuthenticationHandler"
        p:dataSource-ref="dataSource"
        p:tableUsers="users" 
        p:fieldUser="username"
        p:fieldPassword="password" />

In this example, the database contains a table named "users" with columns "username" and "password".

Note you will also need to register a dataSource bean to connect to your specific datasource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment