問題由來
當我們使用CAS來搭建我們的單點登錄系統時,由於CAS默認的登錄認證是簡單的用戶名和密碼相同即可通過認證,所以我們使用時經常需要通過查詢數據庫來認證用戶名和密碼。這便需要擴展CAS驗證的Handler。
解決方案
首先找到CAS中很重要的一個配置文件,deployerConfigContext.xml,在X:\tomcat6\webapps\cas-server\WEB-INF下。找到
1: <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
這段是CAS默認的登錄方式,即用戶名和密碼相同即可通過認證。我們現在需要改造它使之可以通過數據庫查詢的用戶名和密碼進行驗證。
把上面的代碼替換成:
1: <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
2: <property name="sql" value="select password from ucenter_user where username=? " />
3: <property name="dataSource" ref="dataSource" />
4: </bean>
CAS認證的邏輯是,先通過sql獲取該用戶名的密碼,然后用該密碼和用戶名輸入的密碼進行比對,如果相同則通過認證。
dataSource是數據庫配置:
1: <bean id="dataSource"
2: class="org.springframework.jdbc.datasource.DriverManagerDataSource">
3: <property name="driverClassName">
4: <value>oracle.jdbc.driver.OracleDriver</value>
5: </property>
6: <property name="url">
7: <value>jdbc:oracle:thin:@192.168.2.233:1521:splexsrc</value>
8: </property>
9: <property name="username">
10: <value>username</value>
11: </property>
12: <property name="password">
13: <value>passport</value>
14: </property>
15: </bean>
這樣做已基本滿足大部分業務的需求,但如果遇到一些特殊的需求,如要求登錄支持用戶名、郵箱地址、手機號碼。你該怎么辦呢?
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="sql" value="select password from ucenter_user where username=? or email=? or mobile=?" /> <property name="dataSource" ref="dataSource" /> </bean>
是這樣嗎?那我們看看QueryDatabaseAuthenticationHandler.java的源碼:
1: public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
2:
3: @NotNull
4: private String sql;
5:
6: protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
7: final String username = getPrincipalNameTransformer().transform(credentials.getUsername());
8: final String password = credentials.getPassword();
9: final String encryptedPassword = this.getPasswordEncoder().encode(
10: password);
11:
12: try {
13: final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);
14: return dbPassword.equals(encryptedPassword);
15: } catch (final IncorrectResultSizeDataAccessException e) {
16: // this means the username was not found.
17: return false;
18: }
19: }
20:
21: /**
22: * @param sql The sql to set.
23: */
24: public void setSql(final String sql) {
25: this.sql = sql;
26: }
27: }
注意看line 13,QueryDatabaseAuthenticationHandler.java只支持一個參數,也就是說我們前面的寫法是錯誤的,那么找到了原因,解決就很簡單了。
自定義一個驗證Handler,同樣繼承AbstractJdbcUsernamePasswordAuthenticationHandler.java.
1: public class UCQueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
2:
3: @NotNull
4: private String sql;
5:
6: protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
7: final String id = getPrincipalNameTransformer().transform(credentials.getUsername());
8: final String password = credentials.getPassword();
9: final String encryptedPassword = this.getPasswordEncoder().encode(
10: password);
11:
12: try {
13: final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, id,id,id);
14: return dbPassword.equals(encryptedPassword);
15: } catch (final IncorrectResultSizeDataAccessException e) {
16: // this means the username was not found.
17: return false;
18: }
19: }
20:
21: /**
22: * @param sql The sql to set.
23: */
24: public void setSql(final String sql) {
25: this.sql = sql;
26: }
27: }
注意line13,因為傳入的sql=select password from ucenter_user where username=? or email=? or mobile=?。所以這里在查詢時需要配置三個參數,均是用戶名登陸填寫的用戶名(郵箱、手機號)。這樣就滿足了我們的需求,支持用戶名、郵箱、手機號碼登陸。