CAS學習筆記三:SpringBoot自動/手動配置方式集成CAS單點登錄


本文目標

基於SpringBoot + Maven 分別使用自動配置與手動配置過濾器方式集成CAS客戶端。

代碼目錄結構

代碼實現

父工程 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-configdemo-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個過濾器 AuthenticationFilterCas30ProxyReceivingTicketValidationFilter,為便於獲取用戶名配置了 HttpServletRequestWrapperFilter 過濾器,相當於自動配置demo的配置類簡化版,如此而已。

至於為什么用 Cas30ProxyReceivingTicketValidationFilterAbstractTicketValidationFilter 是個抽象類,具體用哪個實現根據協議需要選擇就可以了,更多配置參考官方客戶端源碼倉庫 https://github.com/apereo/java-cas-client

總結

根據本次 集成 CAS 單點登錄代碼的編寫與求證,驗證了上篇文章 CAS 單點登錄的流程,相信大家對CAS更加了解了。

由於行文緊促,可能出現錯誤與遺漏,還請大家多多包涵,評論指出,感激!

本文同步於本人博客園(hellxz.cnblogs.com) 與 CSDN(https://blog.csdn.net/u012586326),禁止轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM