經過一個星期的業余時間研究,終于學會了用cas5.3.9這個版本做單點登錄單點登出功能,還是比較簡單方便的,在此記錄一下。
cas5.3.9是基于springboot開發,只支持1.8jdk以上和tomcat8以上,這里用1.8jdk和tomcat8.5跑cas服務端,用1.8jdk和tomcat7跑普通ssm web客戶端
操作步驟大體可分為以下幾步:
1.去cas官網github地址下載cas5.3.9的cas-overlay-template-5.3.zip
2.根據解壓的這個文件overlay一個cas服務端,基于maven構建方式
3.建立maven cas項目,把生成的cas web服務端文件復制到這個項目中,更改一些必要配置,兼容http訪問和數據庫讀取user表方式驗證,跑起cas服務端
4.ssm web客戶端引入cas必要的jar包依賴,解決jar包沖突問題,配置web.xml實現單點登入登出
5.寫測試頁面測試登入登出功能
一、去cas官網github地址下載cas5.3.9的cas-overlay-template-5.3.zip
地址:https://github.com/apereo/cas-overlay-template/tree/5.3
二、根據解壓的這個文件overlay一個cas服務端,基于maven構建方式
下載解壓之后,按照里面的readme.md指導文件上所說的方式編譯一個cas項目出來
./build.sh help可以看到里面的各種命令
./build.sh clean 清除target
./build.sh package 打包
也可以直接在解壓的文件里直接運行:
mvn clean
mvn install
以上都是linux的構建方式 如果是windows,則要在cmd里打開這個解壓的文件夾運行build.cmd clean
build.cmd package
命令就可以打包啦
有可能會打包失敗,因為國內被墻的原因,下載速度奇慢,所以如果打包失敗了,八成原因是maven庫里的
.m2/repository/org/apereo/cas/cas-server-webapp-tomcat/5.3.9/cas-server-webapp-tomcat-5.3.9.war這個文件下載不成功導致,這個還比較大,有一百多M
所以最簡單的方法就是去一個maven項目里,在pom.xml里加入這個文件的依賴地址,點擊保存就會自動下載到庫里啦,如果maven庫用的是國內比如阿里云的私服就很快下載完啦。
依賴地址:
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-webapp-tomcat</artifactId>
<version>5.3.9</version>
<type>war</type>
</dependency>
下載完這個war包之后,再重新進行上面的打包操作就很快打包成功啦,打包成功后會看到一個target文件夾,里面的cas.war和解壓的cas就是項目代碼啦是可以直接在tomcat8.5下運行的
三、建立maven cas項目,把生成的cas web服務端文件復制到這個項目中,更改一些必要配置,兼容http訪問和數據庫讀取user表方式驗證,跑起cas服務端
用eclipse New一個maven web項目,把解壓出來的cas文件夾下的文件都放在webapp下面
生成的結構如圖:
把WEB-INF/classes/application.properties復制到resources文件夾下
把WEB-INF/classes/services文件夾復制到resources文件夾下
如圖:
更改HTTPSandIMAPS-10000001.json 加入|http
更改application.properties 加入以下配置
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
# Json services \u914D\u7F6E\u4F4D\u7F6E\u8BBE\u5B9A
#cas.serviceRegistry.json.location=classpath:/services
至此加入http協議支持就完成啦
接下來加入jar包支持oracle數據庫,pom.xml里加入以下依賴
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc-drivers</artifactId>
<version>5.3.9</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.jdbc/ojdbc6 -->
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.1.0.6.0</version>
</dependency>
oracle驅動是試了很久才知道必須用ojdbc6的,用其他版本的oracle或許會jar包沖突,項目要求jdk1.8的,ojdbc6是支持1.8的,ojdbc14就不行,上面的兩個cas-server jdbc jar包也必須是5.3.9的 用其他版本也會沖突 如果是mysql數據庫則可以用mysql的數據庫驅動jar包
application.properties里加入數據庫配置
cas.authn.jdbc.query[0].url=jdbc\:oracle\:thin\:@127.0.0.1\:1521\:xe
cas.authn.jdbc.query[0].user=zhaohy
cas.authn.jdbc.query[0].password=oracle
cas.authn.jdbc.query[0].sql=select * from da_user where user_name=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].driverClass=oracle.jdbc.OracleDriver
至此cas就可以讀取數據庫里的user表來完成賬戶用戶名和密碼驗證啦,對了,cas默認的賬戶配置不要忘了注釋掉:
#cas.authn.accept.users=casuser::Mellons
以上,cas的服務端配置就完成了,把cas項目部署到tomcat8.5中可以運行成功,數據庫里面未加密的用戶名密碼可以通過驗證。
項目跑起來后訪問本地cas地址:
http://127.0.0.1:8082/cas/login
可以跳轉到登錄頁面 輸入數據庫中的用戶名和密碼即可登錄成功
補充:
配置cas自帶md5加密方式
只需要在application.properties增加如下代碼:
#配置md5加密
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
數據庫存儲md5加密之后的密碼,再重新啟動cas,輸入明文密碼也可以登錄
配置自定義加密類
自定義加密類必須要實現org.springframework.security.crypto.password.PasswordEncoder這個接口
創建自定義加密類MD5Util
package com.zhaohy.app.util;
import java.security.MessageDigest;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* MD5加密
* @author Administrator
*
*/
public class MD5Util implements PasswordEncoder {
/**
* Title: MD5加密 生成32位md5碼
* Description: TestDemo
* @author lu
* @date 2016年6月23日 下午2:36:07
* @param inStr
* @return 返回32位md5碼
* @throws Exception
*/
public static String md5Encode(String inStr) throws Exception {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
return "";
}
byte[] byteArray = inStr.getBytes("UTF-8");
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
/**
* Title: MD5加密
* Description: TestDemo
* @author lu
* @date 2016年6月23日 下午2:43:31
* @param inStr
* @return
*/
public static String string2MD5(String inStr) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
return "";
}
char[] charArray = inStr.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16)
hexValue.append("0");
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
/**
* Title: 加密解密算法 執行一次加密,兩次解密
* Description: TestDemo
* @author lu
* @date 2016年6月23日 下午2:37:29
* @param inStr
* @return
*/
public static String convertMD5(String inStr) {
char[] a = inStr.toCharArray();
for (int i = 0; i < a.length; i++) {
a[i] = (char) (a[i] ^ 't');
}
String s = new String(a);
return s;
}
public static String md5Decode(String str) {
return convertMD5(convertMD5(str));
}
public static void main(String[] args) {//e10adc3949ba59abbe56e057f20f883e
String s = new String("123456");
System.out.println(md5Decode("a6aeb3ffa55fc7d664406af9c3bd0f1b"));
System.out.println("原始:" + s);
System.out.println("MD5后:" + string2MD5(s));
System.out.println("加密的:" + convertMD5(s));
System.out.println("解密的:" + convertMD5(convertMD5(s)));
}
/**
* 對輸入的密碼加密過程
*/
@Override
public String encode(CharSequence inputPwd) {
System.out.println("inputPwd===" + inputPwd);
System.out.println("string2MD5==" + string2MD5(inputPwd.toString()));
return string2MD5(inputPwd.toString());
}
/**
* 密碼校驗過程
*/
@Override
public boolean matches(CharSequence inputPwd, String dbPwd) {
// 判斷密碼是否存在
if (inputPwd == null) {
return false;
}
System.out.println("inputPwd==" + inputPwd + " " + "dbPwd==" + dbPwd);
if(dbPwd.contentEquals(this.encode(inputPwd))){
return true;
}
return false;
}
}
其中的encode和matches方法是實現PasswordEncoder接口的方法,一個做加密處理,一個做校驗處理。
application.properties增加如下代碼:
#自定義密碼加密方式
cas.authn.jdbc.query[0].passwordEncoder.type=com.zhaohy.app.util.MD5Util
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
重啟cas之后也能登錄成功。
四、ssm web客戶端引入cas必要的jar包依賴,解決jar包沖突問題,配置web.xml實現單點登入登出
建立兩個ssm客戶端項目,分別為ssmTest05,ssmTest05-1
在客戶端項目中pom.xml引入jar包:
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.5.1</version>
<exclusions>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0.pr1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
上面的cas-client-core.3.5.1.jar依賴jackson-databind 2.8.8的版本,但是這個版本跟我用的spring 5.1.8.RELEASE 版本沖突,所以剔除了這個jackson,用了2.10.0.pr1版本的jackson-databind
web.xml配置:
<!-- ======================== 單點登錄開始 ======================== -->
<!-- 用于單點退出,該過濾器用于實現單點登出功能,可選配置-->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<!-- 該過濾器用于實現單點登出功能,可選配置。 -->
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://localhost:8082/cas</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 該過濾器負責用戶的認證工作,必須啟用它 -->
<filter>
<filter-name>CASFilter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>http://127.0.0.1:8082/cas/login</param-value>
</init-param>
<!--
指定客戶端的域名和端口,是指客戶端應用所在機器而不是 CAS Server
-->
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8081</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CASFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 該過濾器負責對Ticket的校驗工作,必須啟用它 -->
<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>http://127.0.0.1:8082/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8081</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
該過濾器負責實現HttpServletRequest請求的包裹,
比如允許開發者通過HttpServletRequest的getRemoteUser()方法獲得SSO登錄用戶的登錄名,可選配置。
-->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>
org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登錄名。
比如AssertionHolder.getAssertion().getPrincipal().getName()。
-->
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- ======================== 單點登錄結束 ======================== -->
<!-- session超時定義,單位為分鐘 -->
<session-config>
<session-timeout>2</session-timeout>
</session-config>
兩個ssm客戶端項目都是這樣配置,至此客戶端就配置完了
cas服務端application.properties文件里加入單點登出設置:
#配置單點登出
#配置允許登出后跳轉到指定頁面
cas.logout.followServiceRedirects=true
#跳轉到指定頁面需要的參數名為 service
cas.logout.redirectParameter=service
#登出后需要跳轉到的地址,如果配置該參數,service將無效。
#cas.logout.redirectUrl=https://www.taobao.com
#在退出時是否需要 確認退出提示 true彈出確認提示框 false直接退出
cas.logout.confirmLogout=false
#是否移除子系統的票據
cas.logout.removeDescendantTickets=true
#禁用單點登出,默認是false不禁止
#cas.slo.disabled=true
#默認異步通知客戶端,清除session
cas.slo.asynchronous=true
至此,整個項目的配置都配置好了
五、寫測試頁面測試登入登出功能
在ssmTest05客戶端里webapp下建立index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
function backToLogin(){
window.location="/ssmTest05/user/logout.do";
}
</script>
<body>
<h2>Hello SpringMVC!</h2><br/>
<button id="button" onclick="backToLogin()">退出</button>
</body>
</html>
在controller里寫一個/user/logout.do接口
@RequestMapping("/user/logout")
public void logout(HttpServletResponse response) {
try {
response.sendRedirect("http://127.0.0.1:8082/cas/logout?service=http://127.0.0.1:8081/ssmTest05/logout.jsp");
} catch (IOException e) {
e.printStackTrace();
}
}
在webapp下再寫一個logout.jsp,退出后重定向到項目會被cas攔截進入登錄頁面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
response.sendRedirect("http://localhost:8081/ssmTest05"); //重定向
%>
</body>
</html>
在ssmTest05-1中webapp下寫一個index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
function backToLogin(){
window.location="/ssmTest05/user/logout.do";
}
</script>
<body>
<h2>Hello SpringMVC-222!</h2><br/>
<button id="button" onclick="backToLogin()">退出</button>
</body>
</html>
運行cas和兩個客戶端項目
訪問
http://127.0.0.1:8081/ssmTest05
會被cas攔截至登錄頁面 輸入數據庫中的用戶名和密碼后成功進入項目
這時訪問
http://127.0.0.1:8081/ssmTest05-1
也可以成功進入第二個項目
隨便在兩個項目中點擊哪個退出按鈕,會跳轉到cas登錄頁面,在不登錄的情況下訪問哪個項目都會被攔截,至此單點登錄和登出就實現成功啦!
在cas客戶端里面如何拿到用戶的登錄信息呢
cas客戶端提供了org.jasig.cas.client.util.AbstractCasFilter這個類
可以在客戶端代碼中加入如下判斷來判斷用戶有沒有登錄以及拿到用戶名
if(null == request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)) {
System.out.println("用戶未登錄!");
} else {
Assertion assertion = (Assertion) request.getSession().getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
AttributePrincipal principal = assertion.getPrincipal();
String username = principal.getName();
System.out.println("userName=op==" + username);
}
自定義login頁面
官方參考文檔:
https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html#themes
https://apereo.github.io/cas/5.3.x/installation/User-Interface-Customization-Themes.html
主題意味著風格不一樣,目的就是不同的接入端,顯示不同的登錄頁面,就像阿里旗下的各種登錄,支付寶登錄,淘寶,天貓,用的可能就是同一個sso,但是各自的登錄主題不一樣。
簡略看完后,會有以下的規范:
● 靜態資源(js,css)存放目錄為src/main/resources/static
● html資源存(thymeleaf)放目錄為src/main/resources/templates
● 主題配置文件存放在src/main/resources并且命名為[theme_name].properties
● 主題頁面html存放目錄為src/main/resources/templates/
自定義login頁面cas5也支持同時定制多種主題,可以為不同客戶端靈活使用,如果不設置主題配置,cas就使用默認主題。
可以定制動態主題和靜態主題,時間有限,只研究了靜態主題,這里只介紹一下靜態主題的配置方法。
在客戶端注冊的json文件中添加theme屬性
如圖,在src/main/resources/services/文件夾下之前已經添加了兩個json文件,
在json文件種添加theme屬性
如:我們在HTTPSandIMAPS-10000001.json里修改
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://127.0.0.1:8081/ssmTest05-1/.*",
"name" : "HTTPS and IMAPS",
"id" : 10000003,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10001,
"theme" : "app2"
}
其中的theme就是指定自定義的主題 ,這里指定app2主題,serviceId是對客戶端的請求使用該主題,可以看到這個主題對ssmTest05-1這個項目的請求使用
在app2.json里修改:
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://127.0.0.1:8081/ssmTest05/.*",
"name" : "HTTPS and IMAPS",
"id" : 10000001,
"description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
"evaluationOrder" : 10000,
"theme" : "app1"
}
這個主題對ssmTest05這個項目的請求使用
在src/main/resources下創建app1.properties 和 app2.properties
根據官網文檔,需要在src/main/resources文件夾的根目錄下創建 與 json文件中theme屬性值 對應的properties
所以要在src/main/resources新建app1.properties 和 app2.properties
app1.properties:
#cas css
cas.standard.css.file=/css/cas.css
#my css
cas.myself.css=/themes/app1/css/cas.css
cas.javascript.file=/themes/app1/js/jquery-3.4.1/jquery-3.4.1.min.js
cas.page.title=app1Theme
app2.properties:
#cas css
cas.standard.css.file=/css/cas.css
#my css
cas.myself.css=/themes/app2/css/cas.css
cas.javascript.file=/themes/app1/js/jquery-3.4.1/jquery-3.4.1.min.js
cas.page.title=app2Theme
在app1.properties 和 app2.properties 中的屬性值都是隨便起,只要在html中指明引用的key就可以了,例如:properties中指明css和js文件地址,然后在html中用下面的方式使用。
<link rel="stylesheet" th:href="@{${#themes.code('cas.myself.css')}}"/>
<script th:src="@{${#themes.code('cas.javascript.file')}}"></script>
注意:上面配置文件中有cas.standard.css.file屬性,這個屬性默認就是指向/css/cas.css也就是cas默認的css文件,這個我們要指明,否則你只是自定義了登錄頁面,其他頁面的樣式將丟失。我們在自定義的登錄頁面使用自己的css文件,不跟cas的默認css混淆。
創建cas.css文件
app1對應的cas.css:
h2 {
color: red;
}
app2對應的cas.css
h2 {
color: blue;
}
在application.properties中添加以下屬性,配置默認主題
# 默認主題
cas.theme.defaultThemeName=app1
配置不同客戶端的登錄頁面
兩個客戶端的頁面都是一樣的,給一個頁面,其他復制就可以了
app1下的 casLoginView.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title th:text="${#themes.code('cas.page.title')}"></title>
<link rel="stylesheet" th:href="@{${#themes.code('cas.myself.css')}}"/>
<script th:src="@{${#themes.code('cas.javascript.file')}}"></script>
</head>
<body>
<h2 th:text="${#themes.code('cas.page.title')}"></h2>
<div>
<form method="post" th:object="${credential}">
<div th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}" style="color: red" />
</div>
<h4 th:utext="#{screen.welcome.instructions}"></h4>
<section class="row">
<label for="username" th:utext="#{screen.welcome.label.netid}" />
<div th:unless="${openIdLocalId}">
<input class="required" id="username" size="25" tabindex="1" type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"
th:value="admin" />
</div>
</section>
<section class="row">
<label for="password" th:utext="#{screen.welcome.label.password}"/>
<div>
<input class="required" type="password" id="password" size="25" tabindex="2"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"
th:value="123456" />
</div>
</section>
<section>
<input type="hidden" name="execution" th:value="${flowExecutionKey}" />
<input type="hidden" name="_eventId" value="submit" />
<input type="hidden" name="geolocation" />
<input class="btn btn-submit btn-block" name="submit" accesskey="l" th:value="#{screen.welcome.button.login}" tabindex="6" type="submit" />
</section>
</form>
</div>
</body>
</html>
搭建完成后目錄如下:
測試
一、先直接登錄cas服務,默認使用的是app1的主題
二、訪問http://127.0.0.1:8081/ssmTest05 顯示app1的主題。
三、訪問http://127.0.0.1:8081/ssmTest05-1 顯示app2的主題。
至此demo已寫完 cas就可以用啦
項目demo代碼已上傳github:
https://github.com/haiyong6/haiyongsRepository/tree/master/code/casDemo