1. 制作及配置安全证书
1) 制作SSL证书
过程不多说,相关资料很多。域名暂定为cas.example.com。证书存放至服务器相关目录,假设为E:HTTPSserverserver.keystore,制作证书时的密码假设为ssl_password。
2) 修改host
用文本编辑器修改C:WindowsSystem32Driversetchost文件,在最后添加一行:
cas.example.com 127.0.0.1
3) 配置tomcat
修改tomcat的server.xml配置文件,在其中添加:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="E:HTTPSserverserver.keystore" keystorePass="ssl_password" truststoreFile="E:HTTPSserverserver.keystore" truststorePass="ssl_password" />
2. 部署cas server 4.2.7
将cas-server-webapp-4.2.7.war拷贝到Tomcat的webapps目录,改名(假设为cas.war);启动Tomcat后,自动解压为cas目录,此时建议删除或移走cas.war。
拷贝以下jar文件至cas/WEB-INF/lib目录:
- cas-server-support-jdbc-4.2.7.jar
- cas-server-support-rest-4.2.7.jar
- ojdbc6.jar或ojdbc7.jar
- cas_custom_auth_handler.jar(自定义密码验证模块)
3. 修改cas server配置
主要是针对两个配置文件deployerConfigContext.xml和cas.properties的修改,均在cas/WEB-INF目录下。
1) 增加数据源
修改deployerConfigContext.xml,添加:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" p:driverClass="oracle.jdbc.driver.OracleDriver" p:jdbcUrl="jdbc:oracle:thin:@localhost:orcl" p:user="oracle" p:password="oracle" p:initialPoolSize="6" p:minPoolSize="6" p:maxPoolSize="18" p:maxIdleTimeExcessConnections="120" p:checkoutTimeout="10000" p:acquireIncrement="6" p:acquireRetryAttempts="5" p:acquireRetryDelay="2000" p:idleConnectionTestPeriod="30" p:preferredTestQuery="select 1" />
2) 修改密码认证方式
修改deployerConfigContext.xml,注释掉原来的primaryAuthenticationHandler,改为:
<!--<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />--> <alias name=" casCustomAuthenticationHandler" alias="primaryAuthenticationHandler" /> <bean id="casCustomAuthenticationHandler" class="com.example.cas_custom_auth_handler.CasCustomAuthenticationHandler" /> <alias name="dataSource" alias="queryDatabaseDataSource" />
3) 修改获取密码的sql
对cas.propeities进行修改,去掉“cas.jdbc.authn.query.sql=”前的注释符,改为适合项目的语句,比如:
cas.jdbc.authn.query.sql=select l.password from user_role r, user_login l where r.user_id=? and r.id=l.id
初始的默认用户已经不再有效,可以注释掉(可选,不影响):
#accept.authn.users=casuser::Mellon
4) 允许注销后可重定向(可选)
修改cas.propeities,去掉“cas.logout.followServiceRedirects=false”前的注释符,改为:
cas.logout.followServiceRedirects=true
5) 修改TGT为永不失效策略
修改deployerConfigContext.xml,注释掉原来的grantingTicketExpirationPolicy,修改为:
<!--<alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" />--> <alias name="neverExpiresExpirationPolicy" alias="grantingTicketExpirationPolicy" />
4. 自定义密码验证模块
这里用我们自定义的密码验证模块cas_custom_auth_handler取代了CAS Server提供的标准验证模块,以下是它的实现代码:
package com.example.cas_custom_auth_handler; import java.security.GeneralSecurityException; import javax.security.auth.login.AccountNotFoundException; import javax.security.auth.login.FailedLoginException; import javax.sql.DataSource; import javax.validation.constraints.NotNull; import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler; import org.jasig.cas.authentication.HandlerResult; import org.jasig.cas.authentication.PreventedException; import org.jasig.cas.authentication.UsernamePasswordCredential; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.stereotype.Component; @Component("casCustomAuthenticationHandler") public class CasCustomAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler { @NotNull private String sql; @Override protected HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential) throws GeneralSecurityException, PreventedException { if (this.sql.isEmpty() || getJdbcTemplate() == null) { throw new GeneralSecurityException("Authentication handler is not configured correctly."); } String username = credential.getUsername(); String password = credential.getPassword(); try { //String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username); String dbPassword = ... // your encryting code here if (!password.equals(dbPassword)) throw new FailedLoginException("Password does not match value on record."); } catch (final IncorrectResultSizeDataAccessException e) { if (e.getActualSize() == 0) throw new AccountNotFoundException(username + " not found with SQL query."); else throw new FailedLoginException("Multiple records found for " + username); } catch (DataAccessException e) { throw new PreventedException("SQL exception while executing query for " + username, e); } return createHandlerResult(credential, this.principalFactory.createPrincipal(username), null); } /** * @param sql: The sql to set. */ @Autowired public void setSql(@Value("${cas.jdbc.authn.query.sql:}") final String sql) { this.sql = sql; } @Override @Autowired(required = false) public void setDataSource(@Qualifier("queryDatabaseDataSource") final DataSource dataSource) { super.setDataSource(dataSource); } }