简介

CAS全称Central Authentication Service,中央认证服务,一种独立开放指令协议。CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目,目前是一种企业级的单点登录解决方案。

协议介绍

关于 oauth2.0 的原理及介绍可以参考理解OAuth 2.0 - 阮一峰的网络日志,这里不做赘述。

环境准备

CAS是基于Spring写的,因此需要准备Java环境,官方提供了一种非常好用的编译方法,我们在使用时可以根据自己的需求来决定依赖的选择,本文主要以openLDAP和oauth为例。编译时需要maven环境。运行时需要 Tomcat 容器,因此需要提前准备好 Tomcat 环境。后续我们会整合 openLDAP 做统一用户管理,因此请先安装好 openLDAP。

编译

首先去项目地址下载编译模板

https://github.com/apereo/cas-overlay-template
# git clone https://github.com/apereo/cas-overlay-template
# cd cas-overlay-template
# vi pom.xml

找到下面的部分

<dependencies>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-webapp${app.server}</artifactId>
        <version>${cas.version}</version>
        <type>war</type>
        <scope>runtime</scope>
    </dependency>
    <!--
    ...Additional dependencies may be placed here...
    -->
    </dependencies>

将注释的部分替换为我们需要的模块

<dependencies>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-webapp${app.server}</artifactId>
        <version>${cas.version}</version>
        <type>war</type>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-oauth-webflow</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.driver.version}</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jdbc</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jdbc-drivers</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <!--
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-rest</artifactId>
        <version>${cas.version}</version>
        <scope>runtime</scope>
    </dependency>
    -->
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-ldap</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <!--
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jpa-ticket-registry</artifactId>
        <version>${cas.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-jpa-service-registry</artifactId>
        <version>${cas.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-rest-services</artifactId>
        <version>${cas.version}</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.apereo.cas</groupId>
        <artifactId>cas-server-support-json-service-registry</artifactId>
        <version>${cas.version}</version>
    </dependency>
    -->
</dependencies>

上面例子中我添加了openLDAP oauth2.0 mysql的依赖,具体请按照自己需求选择。

添加 MySQL 需要指定 connector-java 连接器的版本

<mysql.driver.version>5.1.47</mysql.driver.version>

编辑好pom文件后,执行下面的命令开始编译

# mvn clean package

过程很长,需要联网。如上图所示,编译完成后,会在此目录下生成一个 target 目录,我们需要的 war 包就在里面。

安装测试

安装过程就比较简单了,将 war 包保存至 Tomcat 下 webapps 目录下,然后运行 Tomcat 即可。

# startup.sh
# tail -f /usr/local/tomcat/logs/catalina.out

运行完成后日志如上图所示,然后我们打开 http://localhost:8080/cas

默认的用户名密码为 casuser / Mellon,输入用户名密码点击登录,登录成功后如图跳转至登录成功页面

安装至此完成

开启 oauth 2.0 授权

application.properties 增加配置文件如下

# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/application.properties
cas.authn.oauth.refreshToken.timeToKillInSeconds=2592000
cas.authn.oauth.code.timeToKillInSeconds=30
cas.authn.oauth.code.numberOfUses=1
cas.authn.oauth.accessToken.releaseProtocolAttributes=true
cas.authn.oauth.accessToken.timeToKillInSeconds=7200
cas.authn.oauth.accessToken.maxTimeToLiveInSeconds=28800
cas.authn.oauth.grants.resourceOwner.requireServiceHeader=true
cas.authn.oauth.userProfileViewType=NESTED

增加 OAUTH-1002.json service 注册文件

# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/servies/OAUTH-1002.json
{
  "@class" : "org.apereo.cas.support.oauth.services.OAuthRegisteredService",
  "clientId": "20181124",
  "clientSecret": "123456",
  "serviceId" : "^(https|http|imaps)://.*",
  "name" : "OAuthService",
  "id" : 1002
}

重启 Tomcat 测试

# shutdown.sh                   
Using CATALINA_BASE:   /usr/local/tomcat
Using CATALINA_HOME:   /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
# startup.sh 
Using CATALINA_BASE:   /usr/local/tomcat
Using CATALINA_HOME:   /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
# 

重启完成后,我们利用本博客作为目标访问网址进行测试,浏览器打开 http://localhost:8080/cas/oauth2.0/authorize?response_type=code&client_id=20181124&redirect_uri=https://blog.iamzhl.top

发现跳转了一个示例网址

这时我们需要设置一下两个变量

# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/application.properties

加入下面两行

cas.server.name=http://devops.iamzhl.top:8080/cas
cas.server.prefix=${cas.server.name}

请将 devops.iamzhl.top 改为你的 ip,然后重启 Tomcat 再次测试

这次能正常跳转了,但是出现了未认证授权的服务,这是因为我们没有开启 http 协议支持,因此只要再让我们的 CAS Server 支持 http 认证就行了

# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/application.properties

添加下面两行

cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json

"serviceId" : "^(https|imaps)://.*",改为"serviceId" : "^(https|http|imaps)://.*",,如图

再次登录测试

这次终于正常了,输入用户名密码点击登录,就会跳转到授权页面

点击 Allow 即可成功授权跳转至本博客,我们会注意到 uri 会携带一个 code,这就是 CAS 目前在 oauth2.0 授权中最为完善的 code 授权模式了。

至此, CAS 5.3 集成 oauth2.0 的授权已经搭建完毕

整合 openLDAP

# vi /usr/local/tomcat/webapps/cas/WEB-INF/classes/application.properties

注释掉默认的 cas.authn.accept.users 认证方式并添加与LDAP Server连接的配置(请根据自己的LDAP服务器信息进行修改)

##
# CAS Authentication Credentials
#
# cas.authn.accept.users=casuser::Mellon
cas.authn.ldap[0].principalAttributeList=sn,cn:commonName,givenName,eduPersonTargettedId:SOME_IDENTIFIER
cas.authn.ldap[0].collectDnAttribute=false
cas.authn.ldap[0].principalDnAttributeName=principalLdapDn
cas.authn.ldap[0].allowMultiplePrincipalAttributeValues=true
cas.authn.ldap[0].allowMissingPrincipalAttributeValue=true
cas.authn.ldap[0].credentialCriteria=
cas.authn.attributeRepository.ldap[0].attributes.uid=uid
cas.authn.attributeRepository.ldap[0].attributes.displayName=displayName
cas.authn.attributeRepository.ldap[0].attributes.cn=commonName
cas.authn.attributeRepository.ldap[0].attributes.affiliation=groupMembership
cas.authn.ldap[0].ldapUrl=ldap://devops.iamzhl.top:389
cas.authn.ldap[0].bindDn=cn=Manager,dc=iamzhl,dc=top
cas.authn.ldap[0].bindCredential=passwd
cas.authn.ldap[0].poolPassivator=BIND
cas.authn.ldap[0].connectionStrategy=
cas.authn.ldap[0].providerClass=org.ldaptive.provider.unboundid.UnboundIDProvider
cas.authn.ldap[0].connectTimeout=5000
cas.authn.ldap[0].trustCertificates=
cas.authn.ldap[0].keystore=
cas.authn.ldap[0].keystorePassword=
cas.authn.ldap[0].keystoreType=PKCS12
cas.authn.ldap[0].minPoolSize=3
cas.authn.ldap[0].maxPoolSize=10
cas.authn.ldap[0].validateOnCheckout=true
cas.authn.ldap[0].validatePeriodically=true
cas.authn.ldap[0].validatePeriod=500
cas.authn.ldap[0].validateTimeout=5000
cas.authn.ldap[0].failFast=true
cas.authn.ldap[0].idleTime=500
cas.authn.ldap[0].prunePeriod=24
cas.authn.ldap[0].blockWaitTime=5000
cas.authn.ldap[0].useSsl=false
cas.authn.ldap[0].useStartTls=false
cas.authn.ldap[0].responseTimeout=8000
cas.authn.ldap[0].allowMultipleDns=false
cas.authn.ldap[0].name=
cas.authn.ldap[0].type=AUTHENTICATED
cas.authn.ldap[0].searchFilter=uid={user}
#cas.authn.ldap[0].enhanceWithEntryResolver=true
cas.authn.ldap[0].derefAliases=NEVER
cas.authn.ldap[0].dnFormat=uid=%s,ou=People,dc=iamzhl,dc=top
cas.authn.ldap[0].baseDn=ou=People,dc=iamzhl,dc=top

重启 Tomcat 查看日志

# shutdown.sh
# startup.sh
# tail -f /usr/local/tomcat/logs/catalina.out

新建一个 openLDAP 用户 test / 123456

打开 CAS 网址测试 http://devops.iamzhl.top:8080/cas/login

输入用户名密码登陆成功

日志输出如下

至此,CAS 5.3 整合 openLDAP 结束。

整合 MySQL 认证

配置

修改配置文件

vim /usr/local/tomcat/webapps/cas/WEB-INF/classes/application.properties

最后添加如下内容 (需要将静态认证关闭,即 cas.authn.accept.users 配置项注释掉即可)

##
# MySQL Authentication Credentials
#
# 定义查询用户名的 sql 语句
cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
# 定义用户密码字段
cas.authn.jdbc.query[0].fieldPassword=password
# 定义提示修改密码字段
cas.authn.jdbc.query[0].fieldExpired=expired
# 定义禁用用户的字段
cas.authn.jdbc.query[0].fieldDisabled=disabled
# 使用的数据库方言
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
# 定义数据库类
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
# 使用的数据库
cas.authn.jdbc.query[0].url=jdbc:mysql://localhost:3306/cas?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
# 使用的数据库用户
cas.authn.jdbc.query[0].user=root
# 数据库 root 用户密码
cas.authn.jdbc.query[0].password=123456

#默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5

数据库配置

创建数据库

mysql> create database cas;

创建表

mysql> use cas
mysql> create table sys_user (
 `id` int(11) not null auto_increment,
 `username` varchar(30) not null,
 `password` varchar(64) not null,
 `email`    varchar(50),
 `address`  varchar(100),
 `age`      int,
 `expired` int,
 `disabled` int,
 `locked` int,
  primary key (`id`)
) engine=innodb auto_increment=1 default charset=utf8;

数据表字段说明:

id 为自增字段,username 为用户名,password 为账号密码,email 为邮件地址,address 为联系地址,age 为用户年龄,expired 为是否登录后提示修改密码,disabled 为是否禁用账号,locked 为是否锁定账号,其中 id 为主键自增。

配置完成后在数据表添加用户启动 Tomcat 进行测试即可。

mysql> insert into sys_user values ('1', 'admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin@example.com', '北京顺义', 20, 0, 0, 0);

需要注意的是插入数据时,需要用密码的 MD5 值

自定义登出跳转界面

某些时候,我们可能需要对登出进行定制,比如等出后跳转到等出界面,在CAS 5.3以后的版本中,直接修改前端的HTML页面无法完成登出的定制跳转,不过官方提供了更为方便的设置方法,我们可以通过修改配置文件application.properties来实现,在文件中添加以下内容

cas.logout.followServiceRedirects=true
cas.logout.redirectParameter=service
cas.logout.redirectUrl=https://default.cas.com
cas.logout.confirmLogout=false
cas.logout.removeDescendantTickets=true

请根据需要将其中的redirectUrl改为要定制的登出跳转链接

自定义默认的登录跳转界面

登出跳转可以定制,登录的也可以,有些时候,可能需要在直接访问的CAS的时候,登陆成功后直接跳转到指定的界面,比如直接访问http://devops.iamzhl.top:8080/cas,需要跳转到http://devops.iamzhl.top/index.html,那么就可以在application.properties中添加以下属性

cas.view.defaultRedirectUrl=http://devops.iamzhl.top/index.html

自定义登录界面主题

cas/WEB-INF/classes/static/themes中新建一个文件夹以要设置的主题名命名,比如devops,在此目录下建立jscss以及images等静态资源文件夹,将定制的静态资源保存好,结构如下

然后在application.properties添加以下属性

 cas.theme.defaultThemeName=devops