本文目標
基於SpringBoot + Maven 分別使用自動配置與手動配置過濾器方式整合CAS客戶端。
需要提前搭建 CAS 服務端,參考 https://www.cnblogs.com/hellxz/p/15740935.html
程式碼已上傳本人 GitHub https://github.com/hellxz/cas-integration-demo
程式碼目錄結構
程式碼實現
父工程 cas-integration-demo
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hellxz</groupId>
<artifactId>cas-integration-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<modules>
<!-- 自動配置demo A -->
<module>demo-a-auto-config</module>
<!-- 手動配置demo B -->
<module>demo-b-manual-config</module>
</modules>
<properties>
<java.version>1.8</java.version>
<!-- 指定cas客戶端版本 -->
<cas.client.version>3.6.3</cas.client.version>
</properties>
<dependencies>
<!-- 引入web starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
自動配置工程 demo-a-auto-config
下文簡稱此工程為
服務A
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.hellxz</groupId>
<artifactId>cas-integration-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>demo-a-auto-config</artifactId>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-springboot</artifactId>
<version>${cas.client.version}</version>
</dependency>
</dependencies>
</project>
com.hellxz.cas.CasAutoConfigApp.java
package com.hellxz.cas;
import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.client.boot.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@EnableCasClient //<--自動配置只需要啟用此註解
public class CasAutoConfigApp {
public static void main(String[] args) {
SpringApplication.run(CasAutoConfigApp.class, args);
}
@GetMapping("/test")
public String test(HttpServletRequest request) {
return "服務A測試通過";
}
}
application.properties
server.port=8081
#CAS配置,更多引數見https://github.com/apereo/java-cas-client#spring-boot-autoconfiguration
#cas服務端地址
cas.server-url-prefix=http://192.168.56.104:8080/cas
#cas服務端登入地址
cas.server-login-url=http://192.168.56.104:8080/cas/login
#當前服務地址
cas.client-host-url=http://10.2.6.63:8081
#校驗ticket使用的協議,可選: CAS(代表CAS2)、CAS3、SAML
cas.validation-type=CAS3
注意替換 CAS 服務端地址 及 客戶端 IP
手動配置工程 demo-b-manual-config
下文簡稱此工程為
服務B
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.hellxz</groupId>
<artifactId>cas-integration-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>demo-b-manual-config</artifactId>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${cas.client.version}</version>
</dependency>
</dependencies>
</project>
com.hellxz.cas.CasManualConfigApp.java
package com.hellxz.cas;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class CasManualConfigApp {
public static void main(String[] args) {
SpringApplication.run(CasManualConfigApp.class, args);
}
@GetMapping("/test")
public String test(HttpServletRequest request) {
return "服務B測試通過";
}
}
com.hellxz.cas.Config.java
package com.hellxz.cas;
import java.util.HashMap;
import java.util.Map;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
public class Config {
/**
* 自定義cas服務地址
*/
@Value("${custom.cas.casServerUrlPrefix:}")
private String casServerUrlPrefix;
/**
* 自定義服務標識,格式為{protocol}:{hostName}:{port}
*/
@Value("${custom.cas.serverName:}")
private String serverName;
/**
* 攔截所有請求,將未攜帶票據與會話中無票據的請求都重定向到CAS登入地址
*/
@Bean
@Order(1)
public FilterRegistrationBean<AuthenticationFilter> casAuthenticationFilter() {
FilterRegistrationBean<AuthenticationFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new AuthenticationFilter());
registration.setName("CAS Authentication Filter");
Map<String, String> initParams = new HashMap<>();
initParams.put("casServerUrlPrefix", casServerUrlPrefix); // CAS服務端地址,會拼接為登入地址
initParams.put("serverName", serverName); // 服務地址
registration.setInitParameters(initParams);
registration.addUrlPatterns("/*");
return registration;
}
/**
* 攔截所有請求,使用獲取的票據向CAS服務端發起校驗票據請求
*/
@Bean
@Order(2)
public FilterRegistrationBean<Cas30ProxyReceivingTicketValidationFilter> cas30TicketValidationFilter() {
FilterRegistrationBean<Cas30ProxyReceivingTicketValidationFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
registration.setName("CAS30 Ticket Validation Filter");
Map<String, String> initParams = new HashMap<>();
initParams.put("casServerUrlPrefix", casServerUrlPrefix); // CAS服務端地址,會拼接為服務校驗地址
initParams.put("serverName", serverName);
registration.setInitParameters(initParams);
registration.addUrlPatterns("/*");
return registration;
}
/**
* 包裝HttpServletRequest,使CAS登入成功的使用者名稱等資訊存入請求中<br>
* <br>
* 登入成功後以下兩個方法將不再返回null: <br>
*
* <pre>
* HttpServletRequest#getUserPrincipal()
* HttpServletRequest#getRemoteUser()
* </pre>
*/
@Bean
@Order(3)
public FilterRegistrationBean<HttpServletRequestWrapperFilter> httpServletRequestWrapperFilter() {
FilterRegistrationBean<HttpServletRequestWrapperFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new HttpServletRequestWrapperFilter());
registration.setName("HttpServletRequest Wrapper Filter");
registration.addUrlPatterns("/*");
return registration;
}
}
application.properties
server.port=8082
#cas服務端地址
custom.cas.casServerUrlPrefix=http://192.168.56.104:8080/cas
#當前服務地址
custom.cas.serverName=http://10.2.6.63:8082
注意替換 CAS 服務端地址 及 客戶端 IP
驗證單點登入流程
1、分別啟動 demo-a-auto-config
與 demo-b-manual-config
2、開啟瀏覽器輸入 demo部署的IP:8081/test
,訪問服務A,訪問後立即跳轉CAS登入
3、輸入預設使用者名稱/密碼 casuser/Mellon
,登入成功返回服務A的字樣,讓我們看看登入請求後發生了什麼:
登入成功後,響應頭Set-Cookie回寫了名為 TGC 的Cookie,並且還有 Location 重定向,URL上傳了 ST (服務票據)
重定向到服務A,服務A驗證ST(忽略此流程,它不在瀏覽器端體現),校驗通過回寫Cookie,重定向回服務A的 /test
介面
接著訪問服務A介面,完成響應,此時服務A單點登入已經成功了。
4、清網路記錄,再訪問服務B的介面 服務B地址:8081/test
,可以看到立即跳轉成功了,檢視下請求,主要是 /cas/login 請求
後邊校驗ST重定向與訪問服務A時過程一致,不再贅述。CAS單點登入驗證通過。
程式碼解釋
自動配置demo(服務A)
SpringBoot整合CAS客戶端比較簡單,此處用的是官方方式。首先引入自動配置依賴,並啟用 @EnableCasClient
註解
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-springboot</artifactId>
<version>${cas.client.version}</version>
</dependency>
此依賴引用了 cas-client-core.jar
(核心包)與 cas-client-support-saml.jar
(saml驗籤協議支援),另外 cas-client-support-springboot.jar
中還有配置類
其中:
1、EnableCasClient
註解類引入了配置類 CasClientConfiguration
2、CasClientConfiguration
配置類中使用 CasClientConfigurationProperties
讀取配置檔案,根據配置內容註冊 Spring Bean
3、CasClientConfiguration
配置類中做了以下幾件事:
casAuthenticationFilter()
建立了 認證過濾器casValidationFilter()
建立了 驗證票據過濾器casHttpServletRequestWrapperFilter()
建立了請求物件的包裝類casAssertionThreadLocalFilter()
建立了將Assertion
放到ThreadLocal
的過濾器,對於獲取不到HttpRequest
請求物件的情況這很有用casSingleSignOutFilter()
建立了單點登出的過濾器casSingleSignOutListener()
建立單點登出的Listener,用於監聽登出事件,清理記憶體中單點登入會話快取SpringSecurityAssertionAutoConfiguration
相容Spring Security的配置類
其中對於單點登入最重要的是 casAuthenticationFilter()
、casValidationFilter()
這兩個方法,另外以上幾個方法建立的物件類都在 cas-client-core.jar
,也就是說可以只引這一個包,然後自行配置。
有了自動配置我們再來看手動配置就比較簡單了。
手動配置demo (服務B)
剛才也說了 cas-client-core.jar
是CAS客戶端的核心依賴,必須引用!
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${cas.client.version}</version>
</dependency>
然後就是編寫配置類 com.hellxz.cas.Config.java
,使用 @Value
讀取配置檔案,生成最關鍵的2個過濾器 AuthenticationFilter
與 Cas30ProxyReceivingTicketValidationFilter
,為便於獲取使用者名稱配置了 HttpServletRequestWrapperFilter
過濾器,相當於自動配置demo的配置類簡化版,如此而已。
至於為什麼用
Cas30ProxyReceivingTicketValidationFilter
,AbstractTicketValidationFilter
是個抽象類,具體用哪個實現根據協議需要選擇就可以了,更多配置參考官方客戶端原始碼倉庫 https://github.com/apereo/java-cas-client
總結
根據本次 整合 CAS 單點登入程式碼的編寫與求證,驗證了上篇文章 CAS 單點登入的流程,相信大家對CAS更加了解了。
由於行文緊促,可能出現錯誤與遺漏,還請大家多多包涵,評論指出,感激!
本文同步於本人部落格園(hellxz.cnblogs.com) 與 CSDN(https://blog.csdn.net/u012586326),禁止轉載。