欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

Cas(06)基于数据库的认证

程序员文章站 2024-02-11 16:01:28
...

基于数据库的认证 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。下面来对它们做一个简要的介绍。

1.1 BindModeSearchDatabaseAuthenticationHandler

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">

...

property name="authenticationHandlers">

list>

...

beanclass="org.jasig.cas.adaptors.jdbc.BindModeSearchDatabaseAuthenticationHandler">

property name="dataSource" ref="dataSource"/>

bean>

...

list>

property>

...

bean>

1.2 QueryDatabaseAuthenticationHandler

使用QueryDatabaseAuthenticationHandler需要我们指定一个SQL,该SQL将接收一个用户名作为查询条件,然后返回对应的密码。该SQL将被QueryDatabaseAuthenticationHandler用来通过传入的用户名查询对应的密码,如果存在则将查询的密码与查询出来的密码进行匹配,匹配结果将作为认证结果。如果对应的用户名不存在也将返回false。

以下是QueryDatabaseAuthenticationHandler的一段主要代码:

protected final boolean authenticateUsernamePasswordInternal(finalUsernamePasswordCredentials credentials) throws AuthenticationException {

final String username = getPrincipalNameTransformer().transform(credentials.getUsername());

final String password = credentials.getPassword();

final String encryptedPassword = this.getPasswordEncoder().encode(

password);

try {

final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);

return dbPassword.equals(encryptedPassword);

} catch (final IncorrectResultSizeDataAccessException e) {

// this means the username was not found.

returnfalse;

}

}

上面的逻辑非常明显。此外,如你所见,QueryDatabaseAuthenticationHandler使用的用户名会经过PrincipalNameTransformer进行转换,而密码会经过PasswordEncoder进行编码。Cas Server中基于JDBC的AuthenticationHandler实现中使用到的PrincipalNameTransformer默认是不进行任何转换的NoOpPrincipalNameTransformer,而默认使用的PasswordEncoder也是不会经过任何编码的PlainTextPasswordEncoder。当然了,cas-server-jdbc-support对它们也有另外两种支持,即PrefixSuffixPrincipalNameTransformer和DefaultPasswordEncoder。

1.2.1 PrefixSuffixPrincipalNameTransformer

PrefixSuffixPrincipalNameTransformer的作用很明显,如其名称所描述的那样,其在转换时会将用户名加上指定的前缀和后缀。所以用户在使用的时候需要指定prefix和suffix两个属性,默认是空。

1.2.2 DefaultPasswordEncoder

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 (final NoSuchAlgorithmException e) {

thrownew SecurityException(e);

} catch (final UnsupportedEncodingException e) {

thrownew RuntimeException(e);

}

}

/**

* Takes the raw bytes from the digest and formats them correct.

*

* @param bytes the raw bytes from the digest.

* @return the formatted bytes.

*/

private String getFormattedText(byte[] bytes) {

final StringBuilder buf = new StringBuilder(bytes.length * 2);

for (int j = 0; j length