目录
CAS简介
CAS(Central Authentication Service)是一个
开源的
企业级的
支持多语言(Java, .Net, Python, PHP...)的
SSO(single sign-on, 单点登录)
authentication(认证)
authorization(授权)
解决方案,
- 适用于
web平台
, - 其核心是由Java和Spring实现,
- CAS支持多种协议:
CAS
, SAML v1, SAML v2, WS-Federation,OAuth2, OpenID, OpenID Connect
, REST, - 支持插件式(Pluggable)的认证:
LDAP
,Database
, X.509, SPNEGO, JAAS, JWT, RADIUS, MongoDb, …, - 支持MFA(multifactor authentication)、代理认证(delegated authentication)、实时监控、统一日志管理与输出…
CAS系统包含两个核心组件,即CAS server和CAS client,
且CAS server和CAS clients是一对多的关系,即一个CAS server可以对应多个CAS client。
CAS Server即认证的服务端,负责认证用户,授予用户访问权(即向CAS client颁发tickets),
CAS clients即认证的客户端,需要用户认证的具体web服务,
即指代应用
(任何支持CAS协议的应用 或者 集成client软件包的应用,亦称为CAS applications
,CAS services
),
又指代软件包
(即特定编程语言支持的软件包,通过认证协议如CAS, SAML, OAuth与CAS Server进行交互),
通常没有特殊说明,CAS client即指的软件包
,
例如: Java CAS Client, .NET CAS Client,Pycas, …
CAS协议
关于CAS server和client的具体职责及交互,可参考CAS 3.0.3协议时序图,
整个时序图即描绘了一个完整的SSO过程。
注:
Protected App:受保护的应用,即集成CAS client软件包的web服务,需要用户认证通过后,携带用户凭证才可访问该应用。
关于CAS协议时序图的具体说明如下:
First Access(首次访问web服务)
(1)用户通过浏览器访问web服务(Protected App);
(2)web服务判断用户是否登录,若未登录(没有session cookie或者session不存在,此处的session为web服务对应的session),则重定向到CAS Server登录界面且携带query参数service
(需要URL encoded),该service参数对应CAS Server认证成功后重定向回web服务的url;
302 Location: https://cas.example.com/cas/login?service=https://app.example.com
(3)浏览器重定向到CAS Server登录界面,即请求CAS Server,此时CAS Server检测该请求是否包含SSO Session(CASTGC),若不存在TGC,则返回具体的登录表单form;
TGC(Ticket Granting Cookie)
:CAS server用于存储用户session id的cookie,实际对应CASTGC
TGT(Ticket Granting Ticket)
:代表CAS用户session id,存储在浏览器的TGC cookie中
ST (Service Ticket)
:在CAS Server登录成功后,通过url(即service参数
对应的url)中的GET参数ticket
传递给具体CAS client,后续CAS client需验证该ST并获取到对应的用户信息
(4)用户在CAS登录表单填写正确的用户名密码后,点击提交按钮后将登录请求发送到CAS Server,CAS Server验证用户信息无误后,生成当前用户session并写入CASTGC到浏览器cookie中,并重定向请求回web服务,且重定向url中包含ticket参数
Set-Cookie: CASTGC=TGT-2345678
302 Location: https://app.example.com/?ticket=ST-12345678
(5)浏览器重定向到web服务,即请求web服务(Protected App),web服务需要请求CAS Server来验证url参数ticket(即ST),CAS Server返回一个XML响应体,该XML中包含是否验证成功、被认证的用户信息、可选属性。
# 浏览器重定向到web服务
https://app.example.com/?ticket=ST-12345678
# web服务调用CAS server验证ticket
https://cas.example.com/serviceValidate?service=https://app.example.com&ticket=ST-12345678
(6)验证通过后,web服务可根据CAS验证结果生成当前web服务自身的用户session及cookie,并使浏览器重定向到web服务的具体path(省去ticket参数,避免长时间暴露在浏览器地址栏中)
Set-Cookie: JSESSIONID=ABC1234567
302 Location: https://app.example.com
Second Access To Same Application(后续访问同一个登录过的web服务)
(7)之后再向web服务在发送请求时,即会携带自身的cookie,web服务后端仅需验证session cookie的合法性即可,验证通过后则返回具体请求内容。
Cookie: JSESSIONID=ABC1234567
GET https://app.example.com/
First Access To Second Application(首次访问其他的web服务)
(8)用户通过浏览器访问另一个web服务2(Protected App # 2);
(9)web服务2判断用户是否登录,若未登录(没有session cookie或者session不存在,此处的session为web服务2对应的session),则重定向到CAS Server登录界面且携带query参数service
(需要URL encoded),该service参数对应CAS Server认证成功后重定向回web服务2的url;
302 Location: https://cas.example.com/cas/login?service=https://app2.example.com
注: 其中(8)(9)同之前的(1)(2)
(10)浏览器重定向到CAS Server登录界面,即请求CAS Server,由于之前已在浏览器中登录过CAS Server,所以浏览器中CAS Server域名下已经有CASTGC cookie,故再次向CAS Server发起请求时便会携带该CASTGC cookie,此时CAS Server检测该请求发现已经包含SSO Session(CASTGC),即此时已为登录态,则直接重定向到web服务2且重定向url中包含ticket参数;
# 请求CAS server登录界面
Cookie: CASTGC=TGT-2345678
GET https://cas.example.com/cas/login?service=https://app2.example.com
# 重定向到web服务2
Set-Cookie: CASTGC=TGT-2345678
302 Location: https://app2.example.com/?ticket=ST-3455678
(11)浏览器重定向到web服务2,即请求web服务2(Protected App #2),之后重复第(5)(6)(7)步,
即web服务2调用CAS server验证ticket,web服务2生成自身的用户session及cookie、重定向回web服务2,发送到web服务2的请求携带自身的cookie。
两种session
在整个过程中出现两种session:
(1)CAS Server的用户session,对应CAS server域名下的TGC,该TGC仅对CAS server可见,用来标记用户是否已在CAS server端进行过登录,即全局的登录状态,用于SSO,即由CAS server来统一管理用户登录状态(用户认证),在CAS server中登录成功,则接入该CAS server的CAS Clients均为登录成功,无需用户再次输入账户密码,直接跳转到登录回调URL(对应service参数);
(2)web服务的用户session,对应各自web服务域名下的session cookie,仅对各自的web服务可见,用来标记用户已在web服务中分别登录过,各自集成CAS client的web服务在ST验证通过后需要生成各自的session,即web服务需要维护各自的登录态session;
快速启动CAS server
注: 示例中的cas 6.4.x需要依赖JDK 11,所以需要先安装openjdk11并且设置相关环境变量
(1)下载CAS Initializer并解压缩
https://casinit.herokuapp.com/starter.tgz?type=cas-overlay&baseDir=cas-overlay
注: 详细的可参考:cas/6.4.x - WAR Overlay Initializr
查看README.md内容,并按照内容提示在cas-overlay根目录下依次执行以下命令
(2)构建cas.war
# 构建cas.war(war包输出在build/libs/cas.war)
# Use --refresh-dependencies to force-update SNAPSHOT versions
gradlew clean build
(3)创建keystore(即https证书)
# 创建keystore,
# 如下命令会生成该cas-server对应的https证书,生成的证书存储在根目录/etc/cas下
gradlew createKeystore
注:
在windows环境环境执行执行以上命令会报错
即在windows环境无法定位到根目录/,此处需修改gradle.properties中certDir属性:
...
# certDir由原来的/etc/cas修改为D:/etc/cas
# 在windows系统下根目录即对应盘符根目录,比如cas-overlay在D盘下,则根目录则为D:/,即D:/etc/cas
certDir=D:/etc/cas
serverKeystore=thekeystore
exportedServerCert=cas.crt
storeType=PKCS12
...
查看build.gradle文件中
task createKeystore(group: "CAS", description: "Create CAS keystore") { ... }
发现该createKeystore命令其实对应为如下命令:
# Generating keystore for CAS with DN "CN=cas.example.org,OU=Example,OU=Org,C=US"
keytool -genkeypair -alias cas ^
-keyalg RSA ^
-keypass changeit ^
-storepass changeit ^
-keystore D:/etc/cas/thekeystore ^
-dname CN=cas.example.org,OU=Example,OU=Org,C=US ^
-ext SAN=dns:example.org,dns:localhost,ip:127.0.0.1 ^
-storetype PKCS12
# Exporting cert from keystore...
keytool -exportcert ^
-alias cas ^
-storepass changeit ^
-keystore D:/etc/cas/thekeystore ^
-file D:/etc/cas/cas.crt
同时为了后续的CAS client在通过https请求调用CAS server相关接口(如验证ticket)不发生SSL handshake错误,
需要将生成的D:/etc/cas/cas.crt导入到JDK/JRE中,通过keytool导入命令如下:
# 导入证书
keytool -import -keystore "D:/programs/dev/java/jdk-11/lib/security/cacerts" -file D:/etc/cas/cas.crt -alias cas
# 删除证书(该命令仅在需要删除证书时执行,非必需执行)
keytool -delete -alias cas -keystore "D:/programs/dev/java/jdk-11/lib/security/cacerts"
注:
导入后需重启系统后方可生效,
我本地装有jdk8和jdk11,keytool导入时是在jdk11上执行的,而后续client是执行在jdk8上,
所以client通过https调用CAS server时一直报https验证错误,
后续将client也执行在jdk11上即问题消失,此处需要注意执行keytool导入的jdk和CAS server、CAS client执行的jdk需要保持一致。
(4)拷贝配置
执行如下命令将cas-overlay/etc/cas/config下的配置文件拷贝到根目录下/etc/cas/config(在windows系统下根目录即对应盘符根目录,比如cas-overlay在D盘下,则根目录则为D:/,即D:/etc/cas/config),
在之后启动cas.war时会自动到/etc/cas/config下读取配置文件
gradlew copyCasConfiguration
(5)添加用户
修改之前拷贝的配置文件D:/etc/cas/config/cas.properties,添加如下内容:
# 添加账户(用户名为luo,密码为123456)
cas.authn.accept.users=luo::123456
(6)注册CAS Service(即注册CAS Client应用信息)
修改之前拷贝的配置文件D:/etc/cas/config/cas.properties,添加如下内容使CAS Server从JSON文件中读取service注册信息:
# 从JSON文件读取service注册信息
cas.service-registry.core.init-from-json=true
# service注册对应的JSON文件目录位置
# 可通过classpath:指定resource目录
# 本示例直接指定根目录下/etc/cas/services,即对应D:/etc/cas/services
cas.service-registry.json.location=file:/etc/cas/services
创建service注册对应的JSON文件,建议文件名称遵循{name}-{id}.json
,如下配置即对应casSecureApp-1.json
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
//servcieId通过正则表达式定义client应用的URL pattern,该URL模式应该匹配client的URL
//如"^(https|http)://.*"
"serviceId" : "http://localhost:8900/login/cas",
//client的名称
"name" : "casSecuredApp",
//client的唯一的ID
"id" : 1,
//登出类型
"logoutType" : "BACK_CHANNEL",
//登出回调
"logoutUrl" : "http://localhost:8900/exit/cas"
}
且使用JSON文件注册service需在build.gradle添加依赖如下:
implementation "org.apereo.cas:cas-server-support-json-service-registry"
(7)启动CAS server
# 通过gradle启动
gradlew run
# 或者直接通过java命令启动
java -jar build/libs/cas.war
如下看到READY即为启动成功
(8)访问CAS server
启动成功后即可通过https://localhost:8443/cas进行访问,此时亦可发现之前生成的https证书已生效。
可通过之前第(6)步添加的用户进行登录,即luo/123456,登录成功后如下图
SpringSecurity集成CAS
SpringSecurity集成CAS具体可参见示例代码:https://github.com/marqueeluo/cas-secured-app
CAS Server(即cas-overlay)具体可参见示例代码:https://github.com/marqueeluo/cas-overlay
其中cas-secured-app中的核心cas配置如下:
# CAS server基础URL
cas.server.base-url=https://localhost:8443/cas
# CAS server登录页面URL
cas.server.login-url=https://localhost:8443/cas/login
# CAS server登出URL
cas.server.logout-url=https://localhost:8443/cas/logout
# CAS client基础URL
cas.client.base-url=http://localhost:8900
# CAS client service参数(需匹配CAS service注册中的serviceId)
cas.client.service=http://localhost:8900/login/cas
# CAS client在当前应用中登出cas的urlPath(请求该url则自动重定向到cas.server.logout-url)
cas.client.logout-cas-path=/logout/cas
# CAS client在当前应用中单点登出的回调urlPath
cas.client.slo-callback-path=/exit/cas
后续…
后续有时间会进一步记录
- 关于SpringSecurity集成CAS,具体可参见:https://www.baeldung.com/spring-security-cas-sso
- CAS通过database管理account、service注册,
可参见:
https://apereo.github.io/cas/6.4.x/password_management/Password-Management-JDBC.html
https://apereo.github.io/cas/6.4.x/services/JPA-Service-Management.html - CAS集成OIDC协议,可参见:https://apereo.github.io/cas/6.4.x/protocol/OIDC-Protocol.html
- CAS Logout and Single Logout (SLO)
参考:
https://apereo.github.io/cas/6.4.x/planning/Architecture.html
https://apereo.github.io/cas/6.4.x/installation/WAR-Overlay-Initializr.html
Maven的overlay插件的用法【结合cas4.0.3】
Stackoverflow - Difference Between OAUTH, OpenID and OPENID Connect in very simple term?
https://www.baeldung.com/spring-security-cas-sso
CSDN - CAS服务器5.3搭建