CAS 5.3 环境搭建

711 阅读2分钟

服务端搭建

1.1 使用 cas-overlay-template 搭建cas服务器

GitHub地址:github.com/apereo/cas

maven版本只支持到5.3,下载 5.3.x 即可

1.2 配置外部tomcat容器,启动

默认地址:http://localhost:8443/cas

默认账户密码:casuser / Mellon
账户密码保存: WEB-INF\classes\application.properties

1.3 登录成功如下图所示

客户端接入

2.1 Maven依赖

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.3.0.RELEASE</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.projectlombok</groupId>
    	<artifactId>lombok</artifactId>
    	<version>1.18.12</version>
    </dependency>
    <!--CAS官方客户端包-->
    <dependency>
    	<groupId>org.jasig.cas.client</groupId>
    	<artifactId>cas-client-core</artifactId>
    	<version>3.5.1</version>
    </dependency>
</dependencies>

2.2 配置文件

server:
  port: 8091
spring:
  cas:
#  监听退出的接口,即所有接口都会进行监听
    sign-out-filters: /
#    需要拦截的认证接口
    auth-filters: /*
    validate-filters: /*
    request-wrapper-filters: /*
    assertion-filters: /*
    redirect-after-validation: true
    use-session: true
#    忽略拦截的接口,也就是不用进行拦截,多个可以使用|分隔,遵循正则
    ignore-filters: /test
#    cas服务端地址
    server-url-prefix: http://localhost:8443/cas
    server-login-url: ${spring.cas.server-url-prefix}/login
#    当前项目地址前缀
    client-url-prefix: http://localhost:8091

2.3 CAS参数配置

2.3.1 新建CAS配置参数类 SpringCasProperties
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.List;

/**
 * CAS配置参数
 * @author: Fish
 * @date: 2020/05/19
 **/
@ConfigurationProperties(prefix = "spring.cas")
public class SpringCasProperties {

    private static final String separator = ",";
    /** 拦截url */
    private String validateFilters;
    /** 登出接口 */
    private String signOutFilters;
    /** 需要拦截的认证接口 */
    private String authFilters;
    private String assertionFilters;
    private String requestWrapperFilters;
    /** 需要放行的url,多个可以使用|分隔,遵循正则 */
    private String ignoreFilters;
    private String serverUrlPrefix;
    private String serverLoginUrl;
    /** 当前服务地址前缀 */
    private String clientUrlPrefix;
    private boolean useSession = true;
    private boolean redirectAfterValidation = true;

    public String getIgnoreFilters() {
        return ignoreFilters;
    }

    public void setIgnoreFilters(String ignoreFilters) {
        this.ignoreFilters = ignoreFilters;
    }

    public List<String> getValidateFilters() {
        return Arrays.asList(validateFilters.split(separator));
    }

    public void setValidateFilters(String validateFilters) {
        this.validateFilters = validateFilters;
    }

    public List<String> getSignOutFilters() {
        return Arrays.asList(signOutFilters.split(separator));
    }

    public void setSignOutFilters(String signOutFilters) {
        this.signOutFilters = signOutFilters;
    }

    public List<String> getAuthFilters() {
        return Arrays.asList(authFilters.split(separator));
    }

    public void setAuthFilters(String authFilters) {
        this.authFilters = authFilters;
    }

    public List<String> getAssertionFilters() {
        return Arrays.asList(assertionFilters.split(separator));
    }

    public void setAssertionFilters(String assertionFilters) {
        this.assertionFilters = assertionFilters;
    }

    public List<String> getRequestWrapperFilters() {
        return Arrays.asList(requestWrapperFilters.split(separator));
    }

    public void setRequestWrapperFilters(String requestWrapperFilters) {
        this.requestWrapperFilters = requestWrapperFilters;
    }
    public String getServerUrlPrefix() {
        return serverUrlPrefix;
    }

    public void setServerUrlPrefix(String serverUrlPrefix) {
        this.serverUrlPrefix = serverUrlPrefix;
    }

    public String getServerLoginUrl() {
        return serverLoginUrl;
    }

    public void setServerLoginUrl(String serverLoginUrl) {
        this.serverLoginUrl = serverLoginUrl;
    }

    public String getClientUrlPrefix() {
        return clientUrlPrefix;
    }

    public void setClientUrlPrefix(String clientUrlPrefix) {
        this.clientUrlPrefix = clientUrlPrefix;
    }

    public boolean isRedirectAfterValidation() {
        return redirectAfterValidation;
    }

    public void setRedirectAfterValidation(boolean redirectAfterValidation) {
        this.redirectAfterValidation = redirectAfterValidation;
    }

    public boolean isUseSession() {
        return useSession;
    }

    public void setUseSession(boolean useSession) {
        this.useSession = useSession;
    }
}
2.3.2 新建CAS配置类 SpringCasConfig
import com.fish.casclientdemo1.config.properties.SpringCasProperties;
import lombok.NoArgsConstructor;
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.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * CAS配置
 * @author: Fish
 * @date: 2020/05/19
 **/
@Configuration
@NoArgsConstructor
public class SpringCasConfig {

    @Autowired
    private SpringCasProperties springCasProperties;

    private static boolean casEnabled = true;

    @Bean
    public SpringCasProperties getSpringCasAutoconfig() {
        return new SpringCasProperties();
    }

    @Bean
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>();
        listener.setEnabled(casEnabled);
        listener.setListener(new SingleSignOutHttpSessionListener());
        listener.setOrder(1);
        return listener;
    }

    /**
     * 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean singleSignOutFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new SingleSignOutFilter());
        filterRegistration.setEnabled(casEnabled);
        if (springCasProperties.getSignOutFilters().size() > 0) {
            filterRegistration.setUrlPatterns(springCasProperties.getSignOutFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.addInitParameter("casServerUrlPrefix", springCasProperties.getServerUrlPrefix());
        filterRegistration.setOrder(3);
        return filterRegistration;
    }

    /**
     * 该过滤器负责用户的认证工作
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean authenticationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AuthenticationFilter());
        filterRegistration.setEnabled(casEnabled);
        if (springCasProperties.getAuthFilters().size() > 0) {
            filterRegistration.setUrlPatterns(springCasProperties.getAuthFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        if (springCasProperties.getIgnoreFilters() != null) {
            filterRegistration.addInitParameter("ignorePattern", springCasProperties.getIgnoreFilters());
        }
        filterRegistration.addInitParameter("casServerLoginUrl", springCasProperties.getServerLoginUrl());
        filterRegistration.addInitParameter("serverName", springCasProperties.getClientUrlPrefix());
        filterRegistration.addInitParameter("useSession", springCasProperties.isUseSession() ? "true" : "false");
        filterRegistration.addInitParameter("redirectAfterValidation", springCasProperties.isRedirectAfterValidation() ? "true" : "false");
        filterRegistration.setOrder(4);
        return filterRegistration;
    }

    /**
     * 该过滤器负责对Ticket的校验工作,使用CAS 3.0协议
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean cas30ProxyReceivingTicketValidationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
        filterRegistration.setEnabled(casEnabled);
        if (springCasProperties.getValidateFilters().size() > 0) {
            filterRegistration.setUrlPatterns(springCasProperties.getValidateFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.addInitParameter("casServerUrlPrefix", springCasProperties.getServerUrlPrefix());
        filterRegistration.addInitParameter("serverName", springCasProperties.getClientUrlPrefix());
        filterRegistration.setOrder(5);
        return filterRegistration;
    }

    @Bean
    public FilterRegistrationBean httpServletRequestWrapperFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new HttpServletRequestWrapperFilter());
        filterRegistration.setEnabled(true);
        if (springCasProperties.getRequestWrapperFilters().size() > 0) {
            filterRegistration.setUrlPatterns(springCasProperties.getRequestWrapperFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.setOrder(6);
        return filterRegistration;
    }

    /**
     * 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。
     * 比如AssertionHolder.getAssertion().getPrincipal().getName()。
     * 这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean assertionThreadLocalFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AssertionThreadLocalFilter());
        filterRegistration.setEnabled(true);
        if (springCasProperties.getAssertionFilters().size() > 0) {
            filterRegistration.setUrlPatterns(springCasProperties.getAssertionFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.setOrder(7);
        return filterRegistration;
    }
}
2.3.3 创建页面 hello.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://java.sun.com/JSP/Page">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<text> 这是hello页面,当前用户是:</text>
<text th:text="${username}"></text>
</body>
</html>

controller 类

import lombok.extern.slf4j.Slf4j;
import org.jasig.cas.client.util.AssertionHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author: Fish
 * @date: 2020/05/18
 **/
@Controller
@Slf4j
public class CasController {
    @GetMapping("/hello")
    public String hello(Model model) {
        String username = AssertionHolder.getAssertion().getPrincipal().getName();
        model.addAttribute("username", username);
        log.info("登录用户名:{}", username);
        return "hello";
    }
}

2.4 测试

访问 http://localhost:8091/hello
跳转至cas服务端登录页,登录成功后返回/hello页面代表成功

注意

未认证授权的服务

CAS的服务记录是空的,没有定义服务。 希望通过CAS进行认证的应用程序必须在服务记录中明确定义。

解决方案

  1. 找到 WEB-INF -> classes -> services -> HTTPSandIMAPS-10000001.json

"serviceId" : "^(https|imaps)://.*",

更改为

"serviceId" : "^(https|http|imaps)://.*",
  1. application.properties 新增配置
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
  1. 重新打包即可