xxxxxxxxxx
sudo apt update
sudo apt install -y openjdk-8-jdk
# 验证
java -version
# 输出
# openjdk version "1.8.0_352"
# OpenJDK Runtime Environment (build 1.8.0_352-8u352-ga-1~18.04-b08)
# OpenJDK 64-Bit Server VM (build 25.352-b08, mixed mode)
为支持 SSO(单点登录,Single Sign On),必须通过 HTTPS 登陆 CAS Server,故需要生成证书并导入到 JRE 中。
x
mkdir cas-cert
cd cas-cert/
x
$ keytool -genkey -alias cas.server.com -keyalg RSA -keystore casServer.keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: cas.server.com
What is the name of your organizational unit?
[Unknown]: RD
What is the name of your organization?
[Unknown]: AwesomeCompany
What is the name of your City or Locality?
[Unknown]: Beijing
What is the name of your State or Province?
[Unknown]: Haidian
What is the two-letter country code for this unit?
[Unknown]: zh
Is CN=cas.server.com, OU=RD, O=AwesomeCompany, L=Beijing, ST=Haidian, C=zh correct?
[no]: y
Enter key password for <cas.server.com>
(RETURN if same as keystore password):
Warning:
The JKS keystore uses a proprietary format. It is recommended to migrate to PKCS12 which is an industry standard format using "keytool -importkeystore -srckeystore casServer.keystore -destkeystore casServer.keystore -deststoretype pkcs12".
注意:
keytool -export -alias cas.server.com -keystore casServer.keystore -file casServer.crt -storepass changeit
查看证书:
xxxxxxxxxx
keytool -printcert -file casServer.crt
x
sudo keytool -import -keystore "/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts" -file "casServer.crt" -alias cas.server.com
查看 JDK 证书内容:
x
keytool -list -v -keystore /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts -alias cas.server.com
如果想删除 JDK 证书,那么使用如下命令:
xxxxxxxxxx
sudo keytool -delete -alias cas.server.com -keystore /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacerts
xxxxxxxxxx
sudo mkdir -p /etc/cas && sudo cp casServer.{crt,keystore} /etc/cas/
x
git clone https://github.com/apereo/cas-overlay-template.git -b 5.3
添加华为和 Sonatype 联合发布的中国官方 Maven 仓库:
pom.xml:
x
<repository>
<id>huaweicloud</id>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
在 pom.xml 中
<!--
...Additional dependencies may be placed here...
-->
后面添加:
x
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-ldap</artifactId>
<version>${cas.version}</version>
</dependency>
构建项目:
xxxxxxxxxx
./build.sh package
创建资源目录:
xxxxxxxxxx
mkdir -p src/main/resources
配置资源目录,修改 pom.xml,在 project/build 节点下添加:
x
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
将 war 包下的 application.properties 拷贝到资源目录:
x
cp target/war/work/org.apereo.cas/cas-server-webapp-tomcat/WEB-INF/classes/application.properties src/main/resources/
修改 application.properties:
x
cas.server.name: https://cas.server.com:8443
cas.server.prefix: https://cas.server.com:8443/cas
cas.adminPagesSecurity.ip=127\.0\.0\.1
server.ssl.key-store=file:/etc/cas/casServer.keystore
server.ssl.key-store-password=changeit
server.ssl.key-password=changeit
server.ssl.key-alias=cas.server.com
##
# CAS Authentication Credentials
#
# cas.authn.accept.users=casuser::Mellon
cas.authn.ldap[0].type=AUTHENTICATED
# LDAP 服务地址,如果支持 SSL,那么地址为 ldaps://...
cas.authn.ldap[0].ldapUrl=ldap://127.0.0.1:389
# 是否使用 SSL
cas.authn.ldap[0].useSsl=false
cas.authn.ldap[0].baseDn=dc=mycompany,dc=com
# 用户名匹配规则
cas.authn.ldap[0].searchFilter=(|(cn={user})(uid={user})(mail={user})(mobile={user}))
cas.authn.ldap[0].bindDn=cn=admin,dc=mycompany,dc=com
cas.authn.ldap[0].bindCredential=secret
# 登陆成功后可以查看的信息
cas.authn.ldap[0].principalAttributeList=sn,telephoneNumber,employeeNumber
cas.tgc.secure=false
cas.ticket.tgt.rememberMe.timeToKillInSeconds=3600
cas.ticket.st.timeToKillInSeconds=3600
在 src/main/resources 目录下创建 services 目录:
xxxxxxxxxx
mkdir src/main/resources/services
将 war 包中的 HTTPSandIMAPS-10000001.json 拷贝到 src/main/resources/services 目录:
x
cp target/war/work/org.apereo.cas/cas-server-webapp-tomcat/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json src/main/resources/services/
将其内容修改为:
xxxxxxxxxx
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(http|https|imaps)://.*",
"name" : "HTTP(S) and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000
}
在 application.properties 中添加:
xxxxxxxxxx
cas.serviceRegistry.json.location=classpath:/services
cas.serviceRegistry.initFromJson=true
说明:
- 在测试之前先安装 LDAP
x
sudo ./build.sh run
在测试机上绑定 host:
x# 按需修改 IP 地址
192.168.56.111 cas.server.com
cas_tester.py:
xxxxxxxxxx
import typing
import http
import urllib.parse
import fastapi
import fastapi.responses
import uvicorn
import aiohttp
import xmltodict
app: fastapi.FastAPI = fastapi.FastAPI()
COOKIE_KEY: str = "TEST_CAS_SESSION_ID"
CAS_LOGIN_ENDPOINT: str = "https://cas.server.com:8443/cas/login"
CAS_VALIDATE_ST_ENDPOINT: str = "https://cas.server.com:8443/cas/serviceValidate"
SELF_LOGIN_ENDPOINT: str = "http://192.168.56.1:9876/login"
# 通过中间件统一处理认证
middleware("http") .
async def auth_middleware(request: fastapi.Request, call_next: typing.Callable) -> fastapi.responses.Response:
# 如果调用的是登陆接口
if request.url.path == "/login":
ticket: str = request.query_params.get("ticket", "")
# 如果没有 ticket 参数,那么返回 401
if not ticket:
return fastapi.responses.Response(content="unauthorized", status_code=http.HTTPStatus.UNAUTHORIZED)
# 否则调用 CAS 验证 ST
async with aiohttp.ClientSession() as session:
async with session.get(
CAS_VALIDATE_ST_ENDPOINT,
params={"service": SELF_LOGIN_ENDPOINT, "ticket": ticket},
verify_ssl=False
) as resp:
body = (await resp.content.read())
user: str = xmltodict.parse(body). \
get("cas:serviceResponse", {}). \
get("cas:authenticationSuccess", {}). \
get("cas:user", "")
if user:
response: fastapi.responses.Response = fastapi.responses.RedirectResponse(url="/")
response.set_cookie(COOKIE_KEY, ticket)
response.set_cookie("user_name", user)
return response
else:
return fastapi.responses.PlainTextResponse(
content="Invalid CAS Service Ticket",
status_code=http.HTTPStatus.UNAUTHORIZED
)
# 服务端仅判断请求的 Cookie 中是否包含指定字段,如果包含则认为已登陆,否则认为未登陆
if not request.cookies.get(COOKIE_KEY):
# 如果用户未登陆,那么重定向到 CAS
return fastapi.responses.RedirectResponse(
url=CAS_LOGIN_ENDPOINT + f"?service={urllib.parse.quote(SELF_LOGIN_ENDPOINT)}")
# 如果用户已登陆,那么继续处理
return await call_next(request)
get("/") .
async def index() -> fastapi.responses.PlainTextResponse:
return fastapi.responses.PlainTextResponse(
content="This is Index Page",
status_code=200,
)
if __name__ == "__main__":
# 启动 HTTP Server
uvicorn.run(app, host="0.0.0.0", port=9876)
说明:
Python 版本:3.10.6
依赖包:
- xmltodict==0.13.0
- aiohttp==3.8.3
- uvicorn==0.19.0
- fastapi==0.85.1
操作系统:macOS 12.6
xxxxxxxxxx
wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.69/bin/apache-tomcat-9.0.69.tar.gz
tar zxvf apache-tomcat-9.0.69.tar.gz
将 war 包拷贝到 Tomcat 的 webapps 目录下,比如:
xxxxxxxxxx
cd apache-tomcat-9.0.69/
cp ~/cas-overlay-template/target/cas.war webapps/
修改 conf/server.xml,添加 Connector:
xxxxxxxxxx
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/etc/cas/casServer.keystore"
certificateKeystoreType="JKS" certificateKeystorePassword="changeit" />
</SSLHostConfig>
</Connector>
xxxxxxxxxx
sudo bin/startup.sh
查看日志:
x
sudo tail -f logs/catalina.out
xxxxxxxxxx
sudo bin/shutdown.sh
CAS 除支持 LDAP 认证外,还支持 JDBC 认证、自定义认证、REST 认证。
默认情况下,CAS 使用运行时内存存储 Ticket,当 Web 服务重启时,发出的 Ticket 将丢失。CAS 支持将 Ticket 存储到 Redis 中。
xVagrant.configure("2") do |config|
config.vm.box = "generic/ubuntu1804"
vms = Array(111..111)
vms.each do |seq|
config.vm.define :"cas-#{seq}" do |vagrant|
vagrant.vm.hostname = "cas-#{seq}"
vagrant.vm.network "private_network", ip: "192.168.56.#{seq}"
vagrant.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--name", "cas-#{seq}"]
vb.gui = false
vb.memory = "3072"
vb.cpus = "4"
end
end
end
end