基于数据库的认证 Cas Server自身已经为我们实现了几种基于JDBC的AuthenticationHandler实现,但它们不包含在Cas Server的核心包里面,而是包含在cas-server-support-jdbc中,如果我们要使用Cas Server已经实现好的基于JDBC的AuthenticationHandler,我们必
基于数据库的认证
Cas Server自身已经为我们实现了几种基于JDBC的AuthenticationHandler实现,但它们不包含在Cas Server的核心包里面,而是包含在cas-server-support-jdbc中,如果我们要使用Cas Server已经实现好的基于JDBC的AuthenticationHandler,我们必须先将cas-server-support-jdbc对应的jar包、相关数据库的驱动,以及所需要使用的数据源实现等jar包加入Cas Server的类路径中。如果是基于Maven的war覆盖机制来修改Cas Server的配置文件,则我们可以在自己的Maven项目的依赖中加入如下项(对应的驱动就没贴出来了)。
<dependency>
<groupId>org.jasig.casgroupId>
<artifactId>cas-server-support-jdbcartifactId>
<version>${cas.version}version>
<scope>runtimescope>
dependency>
Cas Server默认已经实现好的基于JDBC的AuthenticationHandler有三个,它们都继承自AbstractJdbcUsernamePasswordAuthenticationHandler,而且在认证过程中都需要一个DataSource。下面来对它们做一个简要的介绍。
BindModeSearchDatabaseAuthenticationHandler将试图以传入的用户名和密码从配置的DataSource中建立一个连接,如果连接成功,则表示认证成功,否则就是认证失败。以下是BindModeSearchDatabaseAuthenticationHandler源码的一段主要代码,通过它我们可以明显的看清其逻辑:
protected final boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials)
throws AuthenticationException {
final String username = credentials.getUsername();
final String password = credentials.getPassword();
try {
final Connection c = this.getDataSource()
.getConnection(username, password);
DataSourceUtils.releaseConnection(c, this.getDataSource());
returntrue;
} catch (final SQLException e) {
returnfalse;
}
}
当然,这种实现也需要你的DataSource支持getConnection(user,password)才行,否则将返回false。dbcp的BasicDataSource的不支持的,而c3p0的ComboPooledDataSource支持。
以下是一个使用BindModeSearchDatabaseAuthenticationHandler的配置示例:
bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
...
属性 名称="authenticationHandlers">
列表>
...
beanclass="org.jasig.cas.adaptors.jdbc.BindModeSearchDatabaseAuthenticationHandler">
属性 名称="数据源" 参考="数据源"/>
豆>
...
列表>
属性>
...
豆子>
使用QueryDatabaseAuthenticationHandler需要我们指定一个SQL,该SQL将接收一个用户名作为查询条件,然后返回应答的密码。该SQL将被QueryDatabaseAuthenticationHandler用于通过确定的用户名查询应答的密码,如果存在则将查询的密码与查询出来的密码进行匹配,匹配结果将作为认证结果。如果对应的用户名不存在也将返回false。
以下是QueryDatabaseAuthenticationHandler的主要一段代码:
受保护 最终 布尔值 authenticateUsernamePasswordInternal(finalUsernamePasswordCredentials 凭据)抛出 AuthenticationException {
最终 字符串用户名 = getPrincipalNameTransformer().transform(credentials.getUsername());
最终 字符串密码 =凭据.getPassword();
最终 字符串加密密码 = 这个.getPasswordEncoder().encode(
密码);
尝试 {
final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.班级, 用户名);
返回 dbPassword.equals(encryptedPassword);
} catch (最终 In CorrectResultSizeDataAccessException e) {
// 表示未找到用户名。
返回假;
}
}
上面的逻辑非常明显。此外,如你所见,QueryDatabaseAuthenticationHandler使用的用户名会经过PrincipalNameTransformer进行转换,而密码会经过PasswordEncoder进行编码。Cas Server中基于JDBC的AuthenticationHandler实现中使用到的PrincipalNameTransformer默认是不进行任何转换的NoOpPrincipalNameTransformer,而默认使用的PasswordEncoder也是不会经过任何编码的PlainTextPasswordEncoder。当然了,cas-server-jdbc-support对它们也有另外两种支持,即PrefixSuffixPrincipalNameTransformer和DefaultPasswordEncoder。
PrefixSuffixPrincipalNameTransformer的作用很明显,如其名称所描述的那样,其在转换时会将用户名加上指定的前缀和后缀。所以用户在使用的时候需要指定prefix和suffix两个属性,默认是空。
DefaultPasswordEncoder底层使用的是标准Java类库中的MessageDigest进行加密的,其支持MD5、SHA等加密算法。在使用时需要通过构造参数encodingAlgorithm来指定使用的加密算法,可以使用characterEncoding属性注入来指定获取字节时使用的编码,不指定则使用默认编码。以下是DefaultPasswordEncoder的源码,其展示了DefaultPasswordEncoder的加密逻辑。
public final class DefaultPasswordEncoder implements PasswordEncoder {
privatestaticfinalchar[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8','9', 'a', 'b', 'c', 'd', 'e', 'f'};
@NotNull
privatefinal String encodingAlgorithm;
private String characterEncoding;
public DefaultPasswordEncoder(final String encodingAlgorithm) {
this.encodingAlgorithm = encodingAlgorithm;
}
public String encode(final String password) {
if (password == null) {
returnnull;
}
try {
MessageDigest messageDigest = MessageDigest
.getInstance(this.encodingAlgorithm);
if (StringUtils.hasText(this.characterEncoding)) {
messageDigest.update(password.getBytes(this.characterEncoding));
} else {
messageDigest.update(password.getBytes());
}
finalbyte[] digest = messageDigest.digest();
return getFormattedText(digest);
} catch (最终 NoSuchAlgorithmException e) {
抛出新 SecurityException(e);
} catch (最终 UnsupportedEncodingException e) {
抛出新 RuntimeException(e);
}
}
/**
* 从摘要中获取原始字节并正确格式化它们。
*
* @param bytes 来自摘要的原始字节。
* @return 格式化字节。
*/
private String getFormattedText(byte[] bytes) {
最终 StringBuilder buf = 新 StringBuilder(字节。长度 * 2);
for (int j = 0; j 长度; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[字节[j] & 0x0f]);
}
返回 buf.toString();
}
public最终void setCharacterEncoding(final 字符串字符编码) {
这个.characterEncoding = characterEncoding;
}
}
如果在认证时需要使用DefaultPasswordEncoder,则需要保证数据库中保存的密码的加密方式和DefaultPasswordEncoder的加密算法及逻辑是一致的。这些都不能满足你的需求,则用户可以实现自己的PrincipalNameTransformer和密码编码器。
以下是一个配置使用QueryDatabaseAuthenticationHandler进行认证,且使用DefaultPasswordEncoder对密码进行MD5加密的示例:
bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
...
属性 名称="authenticationHandlers">
列表>
...
beanclass="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
属性 名称="数据源" 参考="数据源"/>
属性 名称="密码编码器" 参考=“密码编码器”/>
属性 名称="sql" 值=“选择 来自 t_user 的密码,其中用户名 = ?"/>
豆>
...
列表>
属性>
...
豆子>
bean id="密码编码器"类=“org.jasig.cas.authentication.handler.DefaultPasswordEncoder”>
构造函数 值="MD5"/>
豆子>
SearchModeSearchDatabaseAuthenticationHandler 的主要逻辑相当于生成的用户名和密码作为条件从指定的表中进行查询,如果对应记录存在则表示认证通过。使用该AuthenticationHandler 时需要我们指定查询时使用的表名(tableUsers) 、用户名对应的字段名(fieldUser)和密码对应的字段名(fieldPassword)。此外,还可以稀疏的使用PrincipalNameTransformer和PasswordEncoder。以下是SearchModeSearchDatabaseAuthenticationHandler源码中的主要一段代码:
私有 静态最终 字符串 SQL_PREFIX = "选择 从“;
计数('x')
@NotNull
私有 字符串fieldUser;
@NotNull
私有 字符串字段密码;
@NotNull
私有 字符串tableUsers;
私有 字符串sql;
受保护最终布尔值 authenticateUsernamePasswordInternal(finalUsernamePasswordCredentials 凭据)抛出 AuthenticationException {
最终 字符串transformedUsername = getPrincipalNameTransformer().transform(credentials.getUsername());
最终 字符串加密密码 = getPasswordEncoder().encode(credentials.getPassword());
最终int count = getJdbcTemplate().queryForInt(这个.sql,
转换的用户名,加密的密码);
返回 计数>; 0;
}
publicvoid afterPropertiesSet() 抛出 异常 {
这个.sql = SQL_PREFIX + 这个.tableUsers + " 其中“ + 这个。fieldUser
+ " = ? 和 " + 这个.字段密码 + " = ?";
}
以下是一个使用SearchModeSearchDatabaseAuthenticationHandler 的配置示例:
bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
...
属性 名称="authenticationHandlers">
列表>
...
beanclass="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler">
属性 名称="数据源" 参考="数据源"/>
属性 名称="密码编码器" 参考=“密码编码器”/>
属性 名称="tableUsers" 值="t_user"/>
属性 名称="fieldUser" 值="用户名"/>
属性 名称=“字段密码” 值="密码"/>
豆>
...
列表>
属性>
...
豆子>
至此,cas-server-support-jdbc中支持JDBC的三个AuthenticationHandler就讲完了。如果用户觉得它们都不能满足你的要求,则还可以选择使用自己实现的AuthenticationHandler。至于其他认证方式,请参考官方文档。
(注:本文是基于cas 3.5.2所写)
http://blog.csdn.net/elim168/article/details/44420293)