CAS单点登录:集成客户端,自定义client-starter

Posted fdzang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS单点登录:集成客户端,自定义client-starter相关的知识,希望对你有一定的参考价值。

0.说明

本系列文章只根据自己的需要做部分定制,完整的教程见:https://blog.csdn.net/qq_34021712/category_9278675.html

有兴趣的可以一起研究。

1.服务端

1.1.Service配置

客户端接入 CAS 首先需要在服务端进行注册,否则客户端访问将提示“未认证授权的服务”警告:

技术图片

简单需求:对所有https和http请求的service进行允许认证。

在resources/services下新建文件fdzang-1000.json,这个文件是我根据cas源代码HTTPSandIMAPS-10000001.json更改的。路径:casWEB-INFclassesservices

{
  "@class": "org.apereo.cas.services.RegexRegisteredService",
  "serviceId": "^(https|imaps|http)://.*",
  "name": "fdzang",
  "id": 1000,
  "description": "CAS-SSO 登入",
  "evaluationOrder": 10000
}

注意:

services目录中可包含多个 JSON 文件,其命名必须:${name}-${id}.json,id必须为json文件中内容id一致

对其中属性的说明如下,更多详细内容见:官方文档-Service-Management

  • @class:必须为org.apereo.cas.services.RegisteredService的实现类
  • serviceId:对服务进行描述的表达式,可用于匹配一个或多个 URL 地址
  • name: 服务名称 
  • id:全局唯一标志
  •  description:服务描述,会显示在默认登录页
  •  evaluationOrder:定义多个服务的执行顺序

1.2.修改application.properties

配置好service之后,根据官方文档-service-registry,还需修改 application.properties 文件告知 CAS 服务端从本地加载服务定义文件

# 注册客户端
cas.serviceRegistry.initFromJson=true
cas.serviceRegistry.watcherEnabled=true
cas.serviceRegistry.schedule.repeatInterval=120000
cas.serviceRegistry.schedule.startDelay=15000
cas.serviceRegistry.managementType=DEFAULT
cas.serviceRegistry.json.location=classpath:/services
cas.logout.followServiceRedirects=true

2.自定义client-starter

2.1.创建项目

创建一个springboot项目,pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.fdzang</groupId>
    <artifactId>cas-client-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 引用依赖的父包 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jasig.cas.client</groupId>
            <artifactId>cas-client-core</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <!-- 配置该插件将源码放入仓库 -->
            <plugin>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <attach>true</attach>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2.2.加载配置文件

这个类主要的作用是读取client端application.yml中,关于cas相关的配置

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.validation.constraints.NotNull;

@Data
@ConfigurationProperties(prefix = "cas", ignoreUnknownFields = false)
public class CasClientConfigurationProperties {
    /**
     * CAS 服务端 url 不能为空
     */
    @NotNull
    private String serverUrlPrefix;

    /**
     * CAS 服务端登录地址  上面的连接 加上/login 该参数不能为空
     */
    @NotNull
    private String serverLoginUrl;

    /**
     * 当前客户端的地址
     */
    @NotNull
    private String serverName;

    /**
     * 忽略规则,访问那些地址 不需要登录
     */
    private String ignorePattern;

    /**
     * 认证的URL
     */
    private String authrizationUrl;
}

2.3.编写AutoConfigure配置类

这些自动注入的配置,能够让客户端实现接入cas。

import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.AssertionThreadLocalFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableConfigurationProperties(CasClientConfigurationProperties.class)
public class CasClientConfiguration {

    @Autowired
    CasClientConfigurationProperties configProps;

    @Bean
    public FilterRegistrationBean filterSingleRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new SingleSignOutFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(1);

        Map<String, String> params = new HashMap<String, String>();
        params.put("casServerUrlPrefix", configProps.getServerUrlPrefix());
        params.put("serverName", configProps.getServerName());
        registration.setInitParameters(params);

        return registration;
    }

    @Bean
    public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration() {
        ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<EventListener>();
        registrationBean.setListener(new SingleSignOutHttpSessionListener());
        registrationBean.setOrder(1);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean filterAuthenticationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AuthenticationFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(2);

        Map<String, String> params = new HashMap<String, String>();
        params.put("casServerLoginUrl", configProps.getServerLoginUrl());
        params.put("serverName", configProps.getServerName());
        if (!StringUtils.isEmpty(configProps.getIgnorePattern())) {
            params.put("ignorePattern", configProps.getIgnorePattern());
        }
        registration.setInitParameters(params);

        return registration;
    }

    @Bean
    public FilterRegistrationBean filterValidationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(3);

        Map<String, String> params = new HashMap<String, String>();
        params.put("casServerUrlPrefix", configProps.getServerUrlPrefix());
        params.put("serverName", configProps.getServerName());
        params.put("useSession", "true");
        registration.setInitParameters(params);

        return registration;
    }

    @Bean
    public FilterRegistrationBean filterWrapperRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new HttpServletRequestWrapperFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(4);

        return registration;
    }

    @Bean
    public FilterRegistrationBean casAssertionThreadLocalFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AssertionThreadLocalFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(5);

        return registration;
    }
}

2.4.编译基于注解启动方式

客户端只需要在启动类上加上@EnableCasClient注解即可实现接入cas

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(CasClientConfiguration.class)
public @interface EnableCasClient {

}

3.客户端

3.1.新建项目

新建一个springboot项目,pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.fdzang</groupId>
    <artifactId>cas-client</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.9.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.cas.client.version>3.5.0</java.cas.client.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.fdzang</groupId>
            <artifactId>cas-client-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

3.2.配置文件application.yml

server:
  port: 8082

cas:
  server-url-prefix: https://server.cas.com:8443/cas
  server-login-url: https://server.cas.com:8443/cas/login
  server-name: http://127.0.0.1:8082

3.3.主启动类

import com.fdzang.cas.client.configuration.EnableCasClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableCasClient
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}

3.4.测试接口

import com.fdzang.cas.client.configuration.CasClientConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
public class UserController {

    @Autowired
    CasClientConfigurationProperties configProps;

    @GetMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String username = request.getRemoteUser();

        return username;
    }

    @GetMapping("/logout")
    public void loginOut(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String url = configProps.getServerUrlPrefix() + "/logout?service=" + configProps.getServerName();
        response.sendRedirect(url);
    }
}

4.测试

 

以上是关于CAS单点登录:集成客户端,自定义client-starter的主要内容,如果未能解决你的问题,请参考以下文章

cas客户端集成方案

单点登录CAS使用记:实现自定义验证用户登录

单点登录CAS使用记:使用maven的overlay实现无侵入的改造CAS

使用 mod_auth_cas 和自定义登录进行单点登录

终于搞明白了,CAS单点登录原理解析!!

cas5.1.x版本主题界面自定义