CAS Client使用以及执行原理

news2024/9/20 0:54:13

CAS Client使用以及执行原理

流程介绍

CAS Client是利用Java Web中的Filter进行实现认证功能,客户端对CAS Server的认证流程分为以下步骤:

  • 访问CAS Client服务

  • 由于当前session中未检测到认证信息,会重定向到CAS Server地址进行认证

  • 在CAS Server上进行认证

  • 认证通过之后,CAS Server会重定向携带Ticket回到CAS Client地址

  • 将携带的Ticket在后台进行CAS Server校验

  • CAS Server校验Ticket通过之后会返回当前认证的账号信息

  • 在CAS Client拿到CAS Server返回的认证信息之后,缓存到当前session中

在以上流程中,CAS Client使用了两个Filter实现该主要功能。

使用

Maven依赖引入

<dependency>
  <groupId>org.jasig.cas.client</groupId>
  <artifactId>cas-client-core</artifactId>
  <version>3.6.4</version>
</dependency>

这里使用的Spring Boot 2.x版本(即JDK8版本),所以使用的依赖版本为3.6.4版本,如果是Spring Boot 3.x的话(JDK17),应当使用如下依赖:

<dependency>
    <groupId>org.apereo.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>4.0.4</version>
</dependency>

3.6.x以下的版本就不推荐引用了,之前的版本Spring版本为3.x,有兴趣的可以上Github看下官方版本:GitHub - apereo/java-cas-client: Apereo Java CAS Client。

配置

将CAS Client依赖包中的两个Filter过滤器注入到Spring容器即可。

Spring Boot的配置文件,CAS相关

cas:
  server:
    name: http://localhost:9999
    login:
      url: http://localhost:8443/cas/login
    url:
      prefix: http://localhost:8443/cas/
AuthenticationFilter

该过滤器主要是用来检测当前客户端是否通过了CAS Server认证,如果没有通过的话则会调整到CAS Server地址,否则视为通过。

    // 设置cas server的登陆地址
    @Value("${cas.server.login.url}")
    private String casServerLoginUrl;
    // 设置cas server服务前缀,用于拼接cas server服务接口
    @Value("${cas.server.url.prefix}")
    private String casServerUrlPrefix;
    // 设置当前服务名称
    @Value("${cas.server.name}")
    private String serverName;    
​
    @Bean
    public FilterRegistrationBean casClientFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        // 拦截所有接口请求,可根据各自的业务进行配置
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setFilter(new AuthenticationFilter());
        filterRegistrationBean.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        filterRegistrationBean.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        filterRegistrationBean.addInitParameter("serverName", serverName);
        return filterRegistrationBean;
    }
​
TicketValidationFilter

该过滤器是用于获取Ticket以及校验Ticket是否有效,如果检验成功的话,则将校验接口返回的认证用户信息塞到当前session中。

该过滤器有多个版本,这边使用的是如下版本:

    @Bean
    public FilterRegistrationBean ticketValidateFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        // 拦截所有接口请求,可根据各自的业务进行配置
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
        filterRegistrationBean.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        filterRegistrationBean.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        filterRegistrationBean.addInitParameter("serverName", serverName);
        return filterRegistrationBean;
    }

其中配置与上个Filter参数一致即可;总共有四个是实现类,使用CAS20和CAS30的版本较多:

到这CAS Client的配置已经完成了。上述为主要配置,CAS Client中还有一些其他比较好用的Filter便于开发使用,比如AssertionThreadLocalFilter过滤器,将当前认证的用户信息保存到ThreadLocal当中,在开发过程中可以使用AssertionHolder.getAssertion()直接获取到当前用户信息。

执行原理

CAS Client的所有的配置项可以在ConfigurationKeys类中看到配置Key以及默认值;

Filter所有的参数初始化都是在initInternal方法进行的,在看doFilter方法时,需结合initInternal初始化方法一起,这样会了解的比较透彻。

通过解析CAS Client的两个过滤器来看看执行原理,先看看AuthenticationFilter中doFilter方法,重要步骤在如下一步一步说明:

@Override
    public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
​
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 1.判断当前接口请求是否需要被过滤
        if (isRequestUrlExcluded(request)) {
            logger.debug("Request is ignored.");
            filterChain.doFilter(request, response);
            return;
        }
​
        final HttpSession session = request.getSession(false);
        final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
        // 2.判断当前session中是否存在认证用户信息
        if (assertion != null) {
            filterChain.doFilter(request, response);
            return;
        }
        // 3.构建当前请求的服务地址,用于跳转cas server页面拼接的service参数
        final String serviceUrl = constructServiceUrl(request, response);
        // 4.获取当前请求是否存在ticket
        final String ticket = retrieveTicketFromRequest(request);
        // 5.判断当前请求是否为配置的网关地址
        final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
        // 6.如果存在ticket获取是在配置的网关地址中,则直接放开
        if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
            filterChain.doFilter(request, response);
            return;
        }
​
        final String modifiedServiceUrl;
​
        logger.debug("no ticket and no assertion found");
        if (this.gateway) {
            logger.debug("setting gateway attribute in session");
            modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
        } else {
            modifiedServiceUrl = serviceUrl;
        }
​
        logger.debug("Constructed service url: {}", modifiedServiceUrl);
        // 7.需拼接重定向地址页面
        final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
            getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway, this.method);
​
        logger.debug("redirecting to \"{}\"", urlToRedirectTo);
        this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
    }

以上可以综合为,先校验配置的不需要校验的接口,判断当前session是否存在认证的信息,不存在则跳转到指定的CAS Server认证页面。

而在Cas20ProxyReceivingTicketValidationFilter过滤器中,则就是解析Ticket以及将返回的认证用户信息塞到当前session中;

@Override
    public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
                               final FilterChain filterChain) throws IOException, ServletException {
        // 1.检测是否执行代理请求,一般为false,除非一些特殊的业务场景才需要用到
        if (!preFilter(servletRequest, servletResponse, filterChain)) {
            return;
        }
​
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 2.获取Ticket,存在则进入解析,否则直接进入下一个过滤器
        final String ticket = retrieveTicketFromRequest(request);
        
        if (CommonUtils.isNotBlank(ticket)) {
            logger.debug("Attempting to validate ticket: {}", ticket);
​
            try {
                // 3.去请求cas server的接口进行校验Ticket是否正确,正确并返回认证用户信息
                // CAS30是请求/p3/serviceValidate,而CAS20是请求/serviceValidate,两者接口均可以
                final Assertion assertion = this.ticketValidator.validate(ticket,
                        constructServiceUrl(request, response));
​
                logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
               // 4.校验Ticket通过之后,将认证用户信息塞入请求中
                request.setAttribute(CONST_CAS_ASSERTION, assertion);
               // 默认是会塞入到session中,除非特殊业务场景
                if (this.useSession) {
                    request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);
                }
                onSuccessfulValidation(request, response, assertion);
                // 5.默认是重定向到请求地址
                if (this.redirectAfterValidation) {
                    logger.debug("Redirecting after successful ticket validation.");
                    response.sendRedirect(constructServiceUrl(request, response));
                    return;
                }
            } catch (final TicketValidationException e) {
                logger.debug(e.getMessage(), e);
               // Ticket校验失败,封装错误信息返回
                onFailedValidation(request, response);
​
                if (this.exceptionOnValidationFailure) {
                    throw new ServletException(e);
                }
​
                response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
​
                return;
            }
        }
​
        filterChain.doFilter(request, response);
​
    }
​

该过滤器用于校验Ticket并将返回的认证用户信息塞到session中,下次接口请求就无需校验了。

总结

以上为CAS Client的主要流程;在开发过程中大多数都是使用的Spring Boot作为业务开发框架,如果仅仅自己手动配置的CAS Client的话确实有点不和事宜,在CAS官方也提供了自动配置的依赖,目前找到了两种自动配置的依赖:

Unicon网址提供的CAS Client自动配置,该依赖很久没有更新了。

<dependency>
  <groupId>net.unicon.cas</groupId>
  <artifactId>cas-client-autoconfig-support</artifactId>
  <version>2.3.0-GA</version>
</dependency>

CAS官方提供的,分为Spring Boot 2.x以及3.x版本。

<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-support-springboot</artifactId>
    <version>3.6.4</version>
</dependency>
<dependency>
    <groupId>org.apereo.cas.client</groupId>
    <artifactId>cas-client-support-springboot</artifactId>
    <version>4.0.4</version>
</dependency>

两者代码几乎是一致的,参数配置的话均可以根据CasClientConfigurationProperties进行配置即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.Ribbon负载均衡策略及修改

ZoneAvoidanceRule 默认使用的规则 修改规则 第一种方式&#xff1a;定义IRule的Bean,作用于全局。 SpringBootApplication MapperScan("com.xkj.org.mapper") public class OrderApplication {public static void main(String[] args) {SpringApplication.run(Ord…

设计模式——2_A 访问者(Visitor)

文章目录 定义图纸一个例子&#xff1a;如何给好奇宝宝提供他想知道的内容菜单、菜品和配方Menu(菜单) & Cuisine(菜品)Material(物料、食材) 产地、有机蔬菜和卡路里Cuisine & Material 访问者VisitorCuisine & Material 碎碎念访问者和双分派访问者和代理写在最后…

C#基础|StringBuilder字符串如何高效处理。

哈喽&#xff0c;你好&#xff0c;我是雷工。 字符串处理在C#程序开发中是使用频率比较高的&#xff0c;但常规的字符串处理方式对内存占用比较多&#xff0c;为了优化内存&#xff0c;减少不必要的内存浪费&#xff0c;引入了StringBuilder类。 下面学习下StringBuilder类的使…

插入排序动态展示3(Python可视化源代码)

修改了“开始”命令按钮&#xff0c;每次单击“开始”&#xff0c;都重新排序。 Python代码 import tkinter as tk import random import timeclass InsertionSortVisualizer:def __init__(self, root, canvas_width800, canvas_height400, num_bars10):self.root rootself.…

wasm 系列之 WebAssembly 和 emscripten 暴力上手

wasm 是什么&#xff1f; wasm 是 WebAssembly 的缩写。wasm 不是传统意义上的汇编语言&#xff0c;而是一种编译的中间字节码&#xff0c;可以在浏览器和其他 wasm runtime 上运行非 JavaScript 类型的语言&#xff0c;只要能被编译成 wasm&#xff0c;譬如 kotlin/wasm、Rus…

鸿蒙OpenHarmony【轻量系统编写“Hello World”程序】 (基于Hi3861开发板)

编写“Hello World”程序 下方将通过修改源码的方式展示如何编写简单程序&#xff0c;输出“Hello world”。请在下载的源码目录中进行下述操作。 前提条件 已参考鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到…

编写函数fun,它的功能是:根据以下公式求P的值,结果由函数值带回。m与n为两个正整数且要求m>n

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 编写…

【JavaEE初阶系列】——网络层IP协议(地址管理和路由选择)

目录 &#x1f6a9;网络层 &#x1f388;IP协议 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP协议"拆包组包"功能 &#x1f388;地址管理 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP地址的分类 &#x1f469;&#x1f3fb;‍&#x1f4bb;NAT机制如何工作的…

记录:阿里云服务器网站搭建(2)

Docker安装Mysql mysql版本 查看开发环境中mysql版本 &#xff1a;select version()&#xff1b;安装时版本尽量保证一致&#xff0c;最低要求大版本要一致 docker 拉取mysql镜像 docker pull mysql:8.0.36 docker启动mysql容器 docker run -d \ # 创建并运行一个容器&…

下班族张亮的副业赚钱故事

张亮是一个普通的上班族&#xff0c;每天过着朝九晚五的生活。他渴望改变现状&#xff0c;却又觉得生活缺乏突破口。直到有一天&#xff0c;他在网络上偶然发现了水牛社这个平台&#xff0c;这为他打开了一扇新的大门。 张亮开始利用下班后的空闲时间&#xff0c;认真浏览水牛社…

IDEA下载与安装

1.下载 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;7v5q 2.安装

综合案例(前端代码练习):猜数字和表白墙

目录 一、猜数字 html代码&#xff1a; 点击 猜 按钮的js代码&#xff1a; 点击 重开游戏 按钮的js代码&#xff1a; 整体代码&#xff1a; 页面效果&#xff1a; 二、留言板 css代码&#xff1a; html代码&#xff1a; js代码&#xff08;主逻辑在这&#xff09;&am…

JAVA高阶私房菜:JVM虚拟机核心概念及参数微调实验

目录 基础快速掌握 什么是JVM虚拟机 JVM的的实现 操作系统-虚拟机-JRE-JDK的关系 生产环境部署JDK还是JRE JVM内存组成部分和堆空间分布 内存组成 堆空间内存分布 内存分布 堆空间分配 JVM堆空间垃圾回收流程及JVM参数 垃圾回收流程 JVM参数分类 JVM参数格式分类 …

山东大学操作系统实验一(Linux虚拟机实现)

目录 实验题目 实验要求 示例程序 主程序 头文件 重点代码解析 一、main函数的参数 参数介绍 参数输入方式 本块代码 二、信号处理 本块代码 原理介绍 实现效果 三、kill函数 功能介绍 使用方式 本块代码 四、头文件处理 本块代码 代码作用 实验程序 …

Python数据可视化:频率统计条形图countplot()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 Python数据可视化&#xff1a; 频率统计条形图 countplot() [太阳]选择题 请问关于以下代码表述正确的选项是&#xff1f; import seaborn as sns import matplotlib.pyplot as plt data { …

二、python+前端 实现MinIO分片上传

python前端 实现MinIO分片上传 一、背景二、流程图三、代码 一、背景 问题一&#xff1a;前端 -> 后端 ->对象存储 的上传流程&#xff0c;耗费带宽。 解决方案&#xff1a;上传流程需要转化为 前端 -> 对象存储&#xff0c;节省上传带宽 问题二&#xff1a;如果使用…

Leetcode 第394场周赛 问题和解法

题目 统计特殊字母的数量 I 给你一个字符串word。如果word中同时存在某个字母的小写形式和大写形式&#xff0c;则称这个字母为特殊字母。 返回word中特殊字母的数量。 示例 1: 输入&#xff1a;word "aaAbcBC"输出&#xff1a;3解释&#xff1a;word 中的特殊…

【Entity Framework】聊一聊EF如何使用数据库函数

【Entity Framework】聊一聊EF如何使用数据库函数 文章目录 【Entity Framework】聊一聊EF如何使用数据库函数一、数据库函数的类型二、内置函数与用户定义的函数四、聚合函数、标量函数和表值函数五、Niladic函数六、EF Core 中的数据库函数映射6.1 内置函数映射6.2 EF.Functi…

【iOS开发】(四)react Native第三方组件五个20240419-20

react native 外的 第三方组件 目录标题 react native 外的 第三方组件&#xff08;一&#xff09;与rn核心组件的使用步骤区别&#xff1a;&#xff08;二&#xff09;第三方组件概览1 WebView2 Picker3 Swiper4 AsyncStorage5 Geolocation6 Camera (三)详细学习1 WebViewCoco…

ROS1快速入门学习笔记 - 01Linux基础

目录 一、Linux极简基础 二、C与Python极简基础 1. for循环 2. while循环 3. 面向对象 一、Linux极简基础 终端快捷键&#xff1a;ctrlaltt 命令行的操作方式 查看当前终端所在路径&#xff1a;pwd切换路径cd&#xff1b;例如cd /home/ 进入home文件夹&#xff1b;cd …