CAS创建服务端:SSO单点登录(一)创建CAS服务端_我是混IT圈的

由于CAS需要用到域名,所以需要把本地ip映射成域名,参考文档:Win10 如何把本地ip映射成域名_我是混IT圈的

cas客户端示例下载:https://github.com/cas-projects/cas-sample-java-webapp

这个CAS客户端是一个maven项目,可以参考,但是如果想要和springboot整合,可以看下面的配置。

SpringBoot创建CAS客户端如下:

SpringBoot的版本:


<version>2.4.10</version>


pom.xml:


<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <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>

        <!--基于注解的配置 CAS,这个包是必须要的-->
        <dependency>
            <groupId>net.unicon.cas</groupId>
            <artifactId>cas-client-autoconfig-support</artifactId>
            <version>2.0.0-GA</version>
        </dependency>


配置文件:application.properties 


#端口号
server.port=8888
#CAS服务端的地址
cas.server-url-prefix=http://sso.cas.com:8080/cas
#客户端如果要登录,会跳转到统一的服务认证中的登录界面,也就是CAS服务端的登录地址
cas.server-login-url=http://sso.cas.com:8080/cas/login
#客户端在CAS服务端登录成功后,自动从CAS服务端跳转回客户端的地址
cas.client-host-url=http://a.cas.com:8888
#校验后的CAS服务端颁发给客户端的用户信息是否需要放入session中。session获取用户信息,key:_const_cas_assertion_  value:assertion
cas.use-session=true
#Ticket校验器使用 Cas30ProxyReceivingTicketValidationFilter
cas.validation-type=CAS3
#不需要拦截的请求,可以用*做模糊匹配,多个用【|】竖线隔开
udf.ignorePattern=/home.html|/*.js|/*.css
#自定义拦截的路径
udf.ignoreUrlPatternType=com.example.casclienta.config.SimpleUrlPatternMatcherStrategy


配置文件中的一些url,根据实际情况来配置,因为为了让单点登录产生效果,所以必须要用域名才可以。

配置类:CasConfig


package com.example.casclientb.config;

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.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.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * Cas客户端的配置
 */
@Configuration
public class CasConfig {

    @Value("${cas.server-url-prefix}")
    private String serverUrlPrefix;
    @Value("${cas.server-login-url}")
    private String serverLoginUrl;
    @Value("${cas.client-host-url}")
    private String clientHostUrl;
    @Value("${udf.ignorePattern}")
    private String ignorePattern;
    @Value("${udf.ignoreUrlPatternType}")
    private String ignoreUrlPatternType;
    @Value("${cas.use-session}")
    private String useSession;


    /**
     * 登出过滤器(过滤器顺序有要求,先登出-》认证/授权过滤器-》校验过滤器-》request wraper 过滤器)
     * @return
     */
    @Bean
    public FilterRegistrationBean filterSingleRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new SingleSignOutFilter());
        // 设定匹配的路径
        registration.addUrlPatterns("/*");
        Map<String,String> initParameters = new HashMap<>();
        //cas服务端的地址,意思是如果退出了,默认会跳转到这个服务里面去。
        initParameters.put("casServerUrlPrefix", serverUrlPrefix);
        registration.setInitParameters(initParameters);
        // 设定加载的顺序
        registration.setOrder(1);
        return registration;
    }
    /**
     * 添加监听器
     * 它是个登出监听器
     * @return
     */
    @Bean
    public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration() {
        ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<>();
        registrationBean.setListener(new SingleSignOutHttpSessionListener());
        // 设定加载的顺序
        registrationBean.setOrder(1);
        return registrationBean;
    }

    /**
     * 配置认证/授权,也就是登录过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean filterAuthenticationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new AuthenticationFilter());
        // 设定匹配的路径
        registration.addUrlPatterns("/*");
        Map<String,String>  initParameters = new HashMap<>();
        //客户端如果要登录,会跳转到统一的服务认证中的登录界面,也就是CAS服务端的登录地址
        initParameters.put("casServerLoginUrl", serverLoginUrl);
        //客户端在CAS服务端登录成功后,自动从CAS服务端跳转回客户端的地址
        initParameters.put("serverName", clientHostUrl);

        //不需要拦截的请求,必须是正则表达式
        if(ignorePattern != null && !"".equals(ignorePattern)){
            initParameters.put("ignorePattern", ignorePattern);
        }
        //自定义鉴权
        if(ignoreUrlPatternType != null && !"".equals(ignoreUrlPatternType)){
            initParameters.put("ignoreUrlPatternType", ignoreUrlPatternType);
        }
        registration.setInitParameters(initParameters);
        // 设定加载的顺序
        registration.setOrder(2);
        return registration;
    }

    /**
     * 校验/验证过滤器 这里用的是 Cas30ProxyReceivingTicketValidationFilter
     * @return
     */
    @Bean
    public FilterRegistrationBean filterValidationRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
        // 设定匹配的路径
        registration.addUrlPatterns("/*");
        Map<String,String> initParameters = new HashMap<String, String>();
        //CAS服务端的地址
        initParameters.put("casServerUrlPrefix", serverUrlPrefix);
        //校验成功后,自动从CAS服务端跳转回客户端的地址,也是登录成功后跳转的地址
        initParameters.put("serverName", clientHostUrl);
        //校验后的CAS服务端颁发给客户端的用户信息是否需要放入session中。session获取用户信息,key:_const_cas_assertion_  value:assertion
        initParameters.put("useSession", useSession);
        registration.setInitParameters(initParameters);
        // 设定加载的顺序
        registration.setOrder(3);
        return registration;
    }

    /**
     * request wraper 过滤器
     * 重新包装 request 的过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean filterWrapperRegistration() {
        final FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new HttpServletRequestWrapperFilter());
        // 设定匹配的路径
        registration.addUrlPatterns("/*");
        // 设定加载的顺序
        registration.setOrder(4);
        return registration;
    }
}


自定义鉴权类:SimpleUrlPatternMatcherStrategy


import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;

import java.util.regex.Pattern;

/**
 * 自定义鉴权,简单的来说就是自定义需要或者不需要拦截的请求,并且对这些请求可以进行处理
 */
public class SimpleUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {

    //配置文件配置的不需要拦截的请求
    private Pattern pattern;

    @Override
    public boolean matches(String url) {
        //在此可以做额外的扩展,比如判断是insert也放行 可以通过查询数据库来进行动态判断
        if(url.contains("/insert")){
            return true;
        }
        //默认是根据 cas.ignore-pattern 来判断是否否满足过滤
        return this.pattern.matcher(url).find();
    }

    /**
     * 这个pattern就是 在 application.properties 中配置的 udf.ignorePattern
     * @param pattern
     */
    @Override
    public void setPattern(String pattern) {
        this.pattern = Pattern.compile(pattern);
    }
}


HomeController:


package com.example.casclientb.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class HomeController {

    //首页,不需要登录就可以进来
    @GetMapping(value = "/home.html")
    public String index(HttpServletRequest request){
        return "home";
    }

    //详情页面,需要登录
    @GetMapping(value = "/details.html")
    public String list(HttpServletRequest request){
        //cas校验后保存到session中的用户信息,要在session中获取到这个信息,必须在配置中配置。
        Object assertion = request.getSession().getAttribute("_const_cas_assertion_");
        System.out.println(assertion);
        return "details";
    }
}


首页:home.html


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="/static/jquery.min.js"></script>
</head>
<body>
  <p>这里是淘宝首页,不需要登录</p>
    <a href="/details.html">详情</a>
</body>
</html>


详情页:details.html


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="static/jquery.min.js"></script>
</head>
<body>
    <span>这是淘宝详情页面,需要登录</span><br/>
    <a href="http://sso.cas.com:8080/cas/logout?service=http://a.cas.com:8888/home.html">退出</a><br/>
    <a href="/home.html">首页</a><br/>
</body>
</html>


 启动类,添加注解:


//启动CAS @EnableCasClient
@EnableCasClient


由于CAS是默认使用https的,那么为了能测试,需要把CAS服务端配置成能使用http协议。

上面的配置都可以实现单点登录和单点退出的功能。

下一篇文章请期待:shiro和cas的整合。