Contents
  1. 1. 证书的配置
  2. 2. SSO服务端
  3. 3. 导出sso.crt证书
  4. 4. cas服务器端配置Tomcat的HTTPS服务
  5. 5. CAS-数据库帐密认证
    1. 5.1. 重要xml
    2. 5.2. 配置的来由
  6. 6. 本文完

证书的配置

主要分为两大步:服务端生成配置证书.keystore,应用端导入证书sso.crt import to jre/lib/security/cacerts。

SSO服务端

生成keystore, 此文件用于tomcat/conf/server.xml中配置及导出证书;

keytool -genkey -keyalg RSA -alias sso -dname "cn=localhost" 
-keystore /home/ndoc/.keystore -storepass 123654

说明:指定使用RSA算法,生成别名为mlongbosso的证书,口令为123654,证书的DN为”cn=localhost” ,这个DN必须同当前主机完整名称一致!!在正式环境中lo9calhost就应该替换为申请的外网域名),这样,jvm在SSL握手过程中,match域名的时候才能将持有的证书CN与cas-server地址域名成功匹配。

导出sso.crt证书

keytool -export -alias sso -file /home/ndoc/sso.crt 
-keystore /home/ndoc/.keystore -storepass 123654

(注释:从.keystore中导出别名为sso的证书,生成文件sso.crt)

cas服务器端配置Tomcat的HTTPS服务

keystoreFile属性值为.keystore文件路径, keystorePass属性值为证书存贮口令

 <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
        maxThreads="150" scheme="https" secure="true"
        clientAuth="false" sslProtocol="TLS"
         keystoreFile="/home/ndoc/.keystore"
         keystorePass="123654"
          />

应用端
应用端即SSO客户端.

注释: Windows下为%JAVA_HOME% , Linux下为$JAVA_HOME

将sso.crt导入到应用服务器所使用的jre的可信任证书仓库中

keytool -import -alias sso -file /home/ndoc/sso.crt 
-keystore $JAVA_HOME/jre/lib/security/cacerts -storepass 123654

列出jre可信任证书仓库中证书名单,验证导入是否成功,如果导入成功,应该在列表中能找到sso这个别名

keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass 123654
#注意:如果此处导入失败,或者要重新导入,需要先**删除**%JAVA_HOME%/jre/lib/security/cacerts文件(删除前请备份)

为应用服务器开启CAS
应用服务器即SSO客户端。修改web.xml文件,增加如下filter:

<filter>
        <filter-name>CAS Authentication Filter</filter-name>
        <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>
            <param-value>https://localhost:9443/cas/login</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://localhost:7090</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Authentication Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>https://localhost:9443/cas</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <param-value>http://localhost:7090</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

几个配置项说明:

serverName指当前应用的域名地址和端口(80端口可不写)
casServerLoginUrl配置sso登录地址
casServerUrlPrefix设置sso应用地址
url-pattern配置需要sso保护的资源地址
测试客户端配置
测试客户端即测试人员所使用的浏览器端。

在测试浏览器中(受信任的根证书颁发机构项)导入sso.crt证书
访问任意一个需要sso验证的地址
跳转到sso登录界面后,输入正确的用户名和密码
正常返回到原页面

CAS-数据库帐密认证

由于目前cas版本变化带来的内部组件变化,导致配置大相径庭,本文采用CAS-4.2.0配置数据库认证。使用Mysql做数据库,所以将mysql:mysql-connector-driver:5.1.38复制到WEB-INF/lib文件夹下,由于该文件夹下使用了spring-jdbc,所以我们配置datasource bean时使用org.springframework.jdbc.datasource.DriverManagerDataSource

重要xml

可能变化的属性可以配置在cas.properties文件中完成,首先配置完成后的xml如下

cas.jdbc.authn.query.sql=select password from p_user where username=?
<bean id="queryDatabaseDataSource"  
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />  
        <property name="url" value="jdbc:mysql://localhost/privilege" />  
        <property name="username" value="root" />  
        <property name="password" value="" />  
    </bean>  

     <alias name="queryDatabaseAuthenticationHandler" alias="primaryAuthenticationHandler" />
     <alias name="queryDatabaseDataSource" alias="queryEncodeDatabaseDataSource" />
     <alias name="defaultPasswordEncoder" alias="passwordEncoder" />

    <!-- 屏蔽帐号名密码认证bean,并引用到queryDatabaseAuthenticationHandler<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" /> -->

配置的来由

首先打开classes/log4j2.xml文件编辑,打开appender修改修别到info,打印多一些日志。

2016-12-17 21:53:28,242 INFO [org.jasig.cas.authentication.PolicyBasedAuthenticationManager] - QueryDatabaseAuthenticationHandler failed authenticating casuser
2016-12-17 21:53:28,242 DEBUG [org.jasig.cas.authentication.PolicyBasedAuthenticationManager] - QueryDatabaseAuthenticationHandler exception details: Authentication handler is not configured correctly

以下是QueryDatabaseAuthenticationHandler代码

@Component("queryDatabaseAuthenticationHandler")
public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
    @NotNull
    private String sql;
    @Override
    protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
            throws GeneralSecurityException, PreventedException {
            //可以得知sql为空或datasource未设置
        if (StringUtils.isBlank(this.sql) || getJdbcTemplate() == null) {
            throw new GeneralSecurityException("Authentication handler is not configured correctly");
        }
        final String username = credential.getUsername();
        final String encryptedPassword = this.getPasswordEncoder().encode(credential.getPassword());
        try {
            final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);
            if (!dbPassword.equals(encryptedPassword)) {
                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 (final 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);
    }
}

  • 可以看出需要属性cas.jdbc.authn.query.sql即用于查询密码的sql,需要javax.sql.DataSource接口的bean名字为queryDatabaseDataSource
    以下为QueryAndEncodeDatabaseAuthenticationHandler构造函数
    `java

@Autowired(required = false)
public QueryAndEncodeDatabaseAuthenticationHandler(@Qualifier(“queryEncodeDatabaseDataSource”)final DataSource datasource, @Value(“${cas.jdbc.authn.query.encode.sql:}”)final String sql, @Value(“${cas.jdbc.authn.query.encode.alg:}”)final String algorithmName) {

* 可知该bean仍然需要一个queryEncodeDatabaseDataSource为名字的bean,所以
<alias name="queryDatabaseDataSource" alias="queryEncodeDatabaseDataSource" />复制别名

public abstract class AbstractUsernamePasswordAuthenticationHandler extends
AbstractPreAndPostProcessingAuthenticationHandler {
@NotNull
private PasswordEncoder passwordEncoder = new PlainTextPasswordEncoder();
…}
@Component(“plainTextPasswordEncoder”)
public final class PlainTextPasswordEncoder implements PasswordEncoder {
@Override
public String encode(final String password) {
return password;
}
}
@Component(“defaultPasswordEncoder”)
public final class DefaultPasswordEncoder implements PasswordEncoder {

private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
                                            '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final int HEX_RIGHT_SHIFT_COEFFICIENT = 4;
private static final int HEX_HIGH_BITS_BITWISE_FLAG = 0x0f;
private final String encodingAlgorithm;
@Value("${cas.authn.password.encoding.char:}")
private String characterEncoding;

…}
`

  • 如果不设置passwordencoder即为无加密plainTextPasswordEncoder,我们修改别名使用defaultPasswordEncoder即为MD5加密,当然我们可以配置以下属性增加安全性
    • cas.jdbc.authn.query.encode.sql=
    • cas.jdbc.authn.query.encode.alg=
    • cas.jdbc.authn.query.encode.salt.static=
    • cas.jdbc.authn.query.encode.password=
    • cas.jdbc.authn.query.encode.salt=
    • cas.jdbc.authn.query.encode.iterations.field=
    • cas.jdbc.authn.query.encode.iterations=

本文完

Contents
  1. 1. 证书的配置
  2. 2. SSO服务端
  3. 3. 导出sso.crt证书
  4. 4. cas服务器端配置Tomcat的HTTPS服务
  5. 5. CAS-数据库帐密认证
    1. 5.1. 重要xml
    2. 5.2. 配置的来由
  6. 6. 本文完