xxxxxxxxxxsudo apt updatesudo 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-certcd cas-cert/x
$ keytool -genkey -alias cas.server.com -keyalg RSA -keystore casServer.keystoreEnter keystore password:Re-enter new password:What is your first and last name? [Unknown]: cas.server.comWhat is the name of your organizational unit? [Unknown]: RDWhat is the name of your organization? [Unknown]: AwesomeCompanyWhat is the name of your City or Locality? [Unknown]: BeijingWhat is the name of your State or Province? [Unknown]: HaidianWhat is the two-letter country code for this unit? [Unknown]: zhIs 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查看证书:
xxxxxxxxxxkeytool -printcert -file casServer.crtx
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 证书,那么使用如下命令:
xxxxxxxxxxsudo keytool -delete -alias cas.server.com -keystore /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/cacertsxxxxxxxxxxsudo 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创建资源目录:
xxxxxxxxxxmkdir -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:8443cas.server.prefix: https://cas.server.com:8443/cas
cas.adminPagesSecurity.ip=127\.0\.0\.1
server.ssl.key-store=file:/etc/cas/casServer.keystoreserver.ssl.key-store-password=changeitserver.ssl.key-password=changeitserver.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# 是否使用 SSLcas.authn.ldap[0].useSsl=falsecas.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=comcas.authn.ldap[0].bindCredential=secret# 登陆成功后可以查看的信息cas.authn.ldap[0].principalAttributeList=sn,telephoneNumber,employeeNumber
cas.tgc.secure=falsecas.ticket.tgt.rememberMe.timeToKillInSeconds=3600cas.ticket.st.timeToKillInSeconds=3600在 src/main/resources 目录下创建 services 目录:
xxxxxxxxxxmkdir 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 中添加:
xxxxxxxxxxcas.serviceRegistry.json.location=classpath:/servicescas.serviceRegistry.initFromJson=true说明:
- 在测试之前先安装 LDAP
x
sudo ./build.sh run在测试机上绑定 host:
x# 按需修改 IP 地址192.168.56.111 cas.server.com
cas_tester.py:
xxxxxxxxxximport typingimport httpimport urllib.parse
import fastapiimport fastapi.responsesimport uvicornimport aiohttpimport 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
xxxxxxxxxxwget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.69/bin/apache-tomcat-9.0.69.tar.gztar zxvf apache-tomcat-9.0.69.tar.gz将 war 包拷贝到 Tomcat 的 webapps 目录下,比如:
xxxxxxxxxxcd 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>xxxxxxxxxxsudo bin/startup.sh查看日志:
x
sudo tail -f logs/catalina.outxxxxxxxxxxsudo bin/shutdown.shCAS 除支持 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 endend