【CAS6.6源码解析】深度解析默认票据存储策略及其拓展支持-探究存储策略的设计

news2024/9/20 6:42:49

CAS作为一款企业级中央认证服务系统,其票据的生成是非常重要的一环,在票据的生成中,还有一个比较重要的点是票据的存储,本文将默认票据存储策略及其拓展支持,并延伸到探究存储策略的设计。

文章重点分析源码的过程,不想看分析过程可以直接跳到总结处看结论!!!


文章目录

  • A.相关阅读
  • B.涉及源码作用及位置介绍
    • 1.票据存储策略核心源码
    • 2.拓展存储策略支持
  • C.默认存储策略深入解析
    • 1.入口-存储票据
      • 1.1 默认票据存储实例
      • 1.2 默认票据存储策略配置
    • 2.DefaultTicketRegistry分析
      • 2.1 类关系图
      • 2.2 DefaultTicketRegistry
      • 2.3 AbstractMapBasedTicketRegistry
      • 2.4 AbstractTicketRegistry
      • 2.5 涉及设计模式
      • 2.6 小结
    • 3.ST存储策略以及和TGT的关系
      • 3.1 ST创建链路
      • 3.2 ST存储
    • 4.如何做到票据存储策略的模块化拓展
  • 总结
  • 展望
  • 探究存储策略的设计
  • 参考


A.相关阅读

  • 【CAS6.6源码解析】在IDEA中调试可插拔的supprot模块
  • 【CAS6.6源码解析】调试Rest API接口
  • 【CAS6.6源码解析】深入解析TGT和ST的唯一ID是怎样生成的-探究ID生成器的设计

B.涉及源码作用及位置介绍

CAS中,默认支持的是内存的存储策略,涉及存储策略的核心代模块默认会被依赖,但是一些拓展支持的模块如redis存储等属于support模块,需要添加依赖后才会生效。

1.票据存储策略核心源码

1.票据存储策略相关顶级接口在cas-servver-core-api-ticket中的registry包下:

  • TicketRegistry是票据存储的顶级接口,里面规范了一种存储策略需要实现的方法。
  • TicketRegistryCleaner是票据清理的顶级接口,里面规范了一种票据清理器需要实现的方法。
  • TicketRegistrySupport是一个帮助者模式的顶级接口,里面定义了一些需要相互共享和使用的互相不相关的方法。
    在这里插入图片描述

2.上述接口的默认实现类,在cas-server-core-tickets-api模块下的registry包下。

在这里插入图片描述

2.拓展存储策略支持

所有支持的存储策略模块均在support模块下,模块名以-ticket-registry结尾。例如redis支持:
在这里插入图片描述

CAS6.6支持的存储策略有:(13种存储方式)

  • redis
  • couchbase(Couchbase是一个开源的分布式NoSQL文档数据库)
  • couchdb(CouchDB 是一个开源的面向文档的数据库管理系统)
  • dynamodb(AmazonDynamoDB被设计成用来托管的NoSQL数据库服务、可预期的性能、可实现无缝扩展性和可靠性等核心问题)
  • ehcache3(Ehcache 3 是一个强大的缓存技术,它提供了分布式缓存和本地缓存两种模式,并且支持缓存的大小控制、缓存的预热、缓存存储选项和缓存的管理等功能)
  • ehcache(EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider)
  • hazelcast(Hazelcast是一个高度可扩展的数据分发和集群平台,可用于实现分布式数据存储、数据缓存)
  • ignite(追求高性能/高吞吐量和线性扩展能力 关系型数据库缓存)
  • infinispan(一个分布式集群缓存系统)
  • JMS(Java Message Service)
  • JPA
  • memcached(memcached是一套分布式的高速缓存系统)
  • mongodb(基于分布式文件存储的数据库)

C.默认存储策略深入解析

这里以TGT票据的存储为例展开解析,ST与TGT保持一致。

1.入口-存储票据

1.1 默认票据存储实例

DefaultCentralAuthenticationServicecreateTicketGrantingTicket中,创建了TGT后,会通过configurationContext拿到TicketRegistry的一个实例,并且将票据进行存储,如下图:
在这里插入图片描述

1.2 默认票据存储策略配置

查看configurationContext的配置可以发现,默认注入的TicketRegistry的实现类是DefaultTicketRegistry

这里有个很关键的点:TicketRegistry没有实现类时,才会去注入DefaultTicketRegistry,这是完成票据存储拓展支持的核心点。
在这里插入图片描述
并且可以看到传入的参数是一个ConcurrentHashMap,其中初始容量、并发数和加密器是在配置文件中进行配置的。查看默认配置:

在这里插入图片描述
在这里插入图片描述
默认配置里,初始容量是1000,并发数是20,并且默认是不开启加密的。

2.DefaultTicketRegistry分析

从上述入口可以看出默认使用的是DefaultTicketRegistry实现类,并且知道了默认配置参数。接下来就是仔细分析DefaultTicketRegistry这个类的实现了。

2.1 类关系图

类关系图如下:
在这里插入图片描述
可以发现DefaultTicketRegistry继承了AbstractMapBasedTicketRegistry,是一个Map型的存储模式。

2.2 DefaultTicketRegistry

查看其源码:

@Getter
public class DefaultTicketRegistry extends AbstractMapBasedTicketRegistry {

    /**
     * A map to contain the tickets.
     */
    private final Map<String, Ticket> mapInstance;

    public DefaultTicketRegistry() {
        this(CipherExecutor.noOp());
    }
    public DefaultTicketRegistry(final CipherExecutor cipherExecutor) {
        super(cipherExecutor);
        this.mapInstance = new ConcurrentHashMap<>();
    }
    public DefaultTicketRegistry(final Map<String, Ticket> storageMap, final CipherExecutor cipherExecutor) {
        super(cipherExecutor);
        this.mapInstance = storageMap;
    }
}

主要就是将配置好的ConcurrentHashMap传给父类AbstractMapBasedTicketRegistry

2.3 AbstractMapBasedTicketRegistry

基于Map进行存储的逻辑在AbstractMapBasedTicketRegistry中。

@Slf4j
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class AbstractMapBasedTicketRegistry extends AbstractTicketRegistry {

    protected AbstractMapBasedTicketRegistry(final CipherExecutor cipherExecutor) {
        setCipherExecutor(cipherExecutor);
    }

    @Override
    public void addTicketInternal(final Ticket ticket) throws Exception {
        val encTicket = encodeTicket(ticket);
        LOGGER.debug("Putting ticket [{}] in registry.", ticket.getId());
        getMapInstance().put(encTicket.getId(), encTicket);
    }

    @Override
    public Ticket getTicket(final String ticketId, final Predicate<Ticket> predicate) {
        val encTicketId = encodeTicketId(ticketId);
        if (StringUtils.isBlank(ticketId)) {
            return null;
        }
        val found = getMapInstance().get(encTicketId);
        if (found == null) {
            LOGGER.debug("Ticket [{}] could not be found", encTicketId);
            return null;
        }

        val result = decodeTicket(found);
        if (!predicate.test(result)) {
            LOGGER.debug("Cannot successfully fetch ticket [{}]", ticketId);
            return null;
        }
        return result;
    }

    @Override
    public long deleteSingleTicket(final String ticketId) {
        val encTicketId = encodeTicketId(ticketId);
        return !StringUtils.isBlank(encTicketId) && getMapInstance().remove(encTicketId) != null ? 1 : 0;
    }

    @Override
    public long deleteAll() {
        val size = getMapInstance().size();
        getMapInstance().clear();
        return size;
    }

    @Override
    public Collection<? extends Ticket> getTickets() {
        return decodeTickets(getMapInstance().values());
    }

    @Override
    public Ticket updateTicket(final Ticket ticket) throws Exception {
        LOGGER.trace("Updating ticket [{}] in registry...", ticket.getId());
        addTicket(ticket);
        return ticket;
    }

    /**
     * Create map instance, which must ben created during initialization phases
     * and always be the same instance.
     *
     * @return the map
     */
    public abstract Map<String, Ticket> getMapInstance();
}

梳理一下核心逻辑,可以发现,在此类中实现的方法,仅仅是和票据存储相关的(存储,编码解码),其余票据存储的前后逻辑,仍在其父类AbstractTicketRegistry中。

2.4 AbstractTicketRegistry

分析其最常用的增删改查方法:(代码过长,只贴部分)

1.存储票据时只校验一下是否过期(过期策略不是本章的重点),具体存储操作交由其子类来处理。
在这里插入图片描述
2.获取票据时会依据提供的类进行强转,每个票据获取时还会进行过期校验,如果过期会直接删除。

在这里插入图片描述
在这里插入图片描述

3.删除票据时,如果是TGT,还会将其授予的ST全部删除。

在这里插入图片描述

2.5 涉及设计模式

AbstractTicketRegistry类采用模版方法模式将具体的存储操作交由子类完成,拓展了存储的多样性。

AbstractMapBasedTicketRegistry类采用模版方法模式将Map的实例化交由其子类来完成,拓展了Map的多样性。

2.6 小结

DefaultTicketRegistry本质是将票据存储在ConcurrentHashMap中,将其初始容量,并发数,加密器拓展成了配置,并有默认配置。

DefaultTicketRegistry进行了分层设计,从顶级抽象类到该类,每个中间类都只是完成它管辖范围内的操作,其余操作交由其子类来具体实现。

3.ST存储策略以及和TGT的关系

上述是TGT的默认存储策略,我们来看一下ST是如何存储的。

3.1 ST创建链路

授予ST的入口在DefaultCentralAuthenticationServicegrantServiceTicket方法中,核心代码如下:

在这里插入图片描述
首先拿到ST的factory,通过factory创建ST,然后更新TGT,最后将ST进行存储。

其最终授予ST的核心代码如下:是通过ticketGrantingTicketgrantServiceTicket进行授予ST。
在这里插入图片描述
在这里插入图片描述

其中,在新建ST对象的时候,会关联TGT:
在这里插入图片描述

同时,在trackingPolicy.track(this, serviceTicket);中,会将ST关联的TGT的services MAP中。

在这里插入图片描述

其余票据生成过程不是本章关心的内容,这里主要分析其存储的关联关系。

总结:ST是由TGT实现类的某个方法授予的,ST在初始化的时候,指定了TGT属性进行关联,ST创建完成后,TGT会将ST加入到一个services的map中进行关联。在删除TGT的时候,也会将其关联的ST全部删除。

3.2 ST存储

存储ST的存储策略仍然是通过configurationContext.getTicketRegistry()获取的,与TGT完全一致。

4.如何做到票据存储策略的模块化拓展

以redis为例,分析如何拓展支持一种存储策略。

cas-server-support-redis-ticket-registry模块的config包下,注入了RedisTicketRegistry

在这里插入图片描述
注意此处是直接用的@Bean申明为一个实体,而在CasCoreTicketsConfiguration中,如果已经有Ticketregistry的实体,将不会再注入默认的票据存储策略。

在这里插入图片描述
此时Spring容器里面,Ticketregistry的实现实体就只有RedisTicketRegistry,那么在通过configurationContext.getTicketRegistry()获取票据存储策略的时候,得到的就是RedisTicketRegistry

注意,拓展支持的存储策略模块中的配置,都是使用@Bean进行注入的,并未申明对象名字,那么如果同时开启多个存储策略模块,SpringBoot将无法成功启动!


总结

  • CAS6.6中通过默认存储策略的@ConditionalOnMissingBean(name = TicketRegistry.BEAN_NAME)注解和拓展支持类中的@Bean实现了默认票据存储策略及其它拓展票据存储策略的支持。
  • DefaultTicketRegistry本质是将票据存储在ConcurrentHashMap中,将其初始容量,并发数,加密器拓展成了配置,并有默认配置。
  • DefaultTicketRegistry进行了分层设计,从顶级抽象类到该类,每个中间类都只是完成它管辖范围内的操作,其余操作交由其子类来具体实现。是一种经典的模版方法模式。
  • ST和TGT进行了关联,在删除TGT的时候,同时会删除ST。
  • 若需要新增一种存储策略,只需要依赖新模块后,用@Bean注解将Ticketregistry的新实现类注入到容器中,即可完成拓展。

展望

本文只着重分析了TicketRegistry下默认的票据存储策略和拓展支持的分析,对票据过期策略和票据清理策略等的设计还未分析,预计会在未来详细分析这些模块。

探究存储策略的设计

参考CAS的思路,为某种数据设计存储策略时,若要保障足够的拓展性,可以从以下几个方面进行考虑:

  • 将存储的过程进行详细的拆分,设计多级接口多级抽象类,每个类完成指定范围内的工作,剩下的操作使用模版方法模式拓展给子类进行实现。
  • 将凡是可能变的参数配置在配置文件中,并提供默认配置,这样能通过配置文件完成高度的拓展。
  • 通过使用@ConditionalOnMissingBean注解的方式为顶级接口注入默认的实现类,若要拓展出一种其他的存储策略,只需要实现顶级接口,并使用@Bean注入容器中,即可实现。

参考

截止2023-07-31为止,还没有专门分析CAS6源码的文章可检索,本文只参考了CAS6.6的源代码,所有分析过程均经过动态调试验证。


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

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

相关文章

一起学算法(二分查找篇)

1.线性枚举 1.线性枚举定义 线性枚举指的就是遍历某个一维数组&#xff08;顺序表&#xff09;的所有元素&#xff0c;找到满足条件的那个元素并且返回&#xff0c;返回值可以是下标&#xff0c;也可以是元素本身。 由于是遍历的&#xff0c;穷举了所有的情况&#xff0c;所以…

iphone卡在恢复模式怎么办?修复办法分享!

iPhone 卡在恢复屏幕问题是 iPhone 用户在软件更新或恢复期间的常见问题。如果你也遇到此问题&#xff0c;不要着急&#xff0c;接下来我们将探讨 iPhone 卡在恢复屏幕上的主要原因&#xff0c;以及如何轻松修复它。 iPhone卡在恢复屏幕问题上没有一个特别的原因&#xff0c;但…

ALLEGRO之Route菜单

本文主要介绍了ALLEGRO的Route菜单。 &#xff08;1&#xff09;Connect&#xff1a;走线&#xff1b; &#xff08;2&#xff09;Slide&#xff1a;推挤&#xff1b; &#xff08;3&#xff09;Timing Vision&#xff1a;等长设计时使用&#xff1f;暂不清楚&#xff1b; &…

【动态规划算法】-子序列问题题型(27-34题)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

svn还原本地代码

svn代码还原 问题描述&#xff1a;在vscode中修改了代码&#xff0c;没有提交&#xff0c;而且不小心点击了svn更新&#xff0c;导致本地修改的最新代码被覆盖&#xff0c;因为没有提交&#xff0c;所以远程仓库中也没有刚才修改的代码记录 解决&#xff1a; 通过vscode的时间…

远距离传输大型文件:如何应对不同地区的网络环境和带宽约束

在现代社会中&#xff0c;远程传输大文件已经变得非常常见了。无论是个人生活还是各种组织之间的合作和协作&#xff0c;都需要频繁地进行文件传输。但是&#xff0c;由于不同地区的网络状况和带宽限制&#xff0c;传输大文件可能会遇到很多问题。因此&#xff0c;如何应对不同…

PSP - 蛋白质序列搜索算法 MMseqs2 与 HHblits 的搜索结果差异

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132025401 在 AlphaFold2 中&#xff0c;使用 HHblits 算法搜索 BFD 与 UniRef30&#xff0c;输出 bfd_uniref_hits.a3m 文件。MMseqs2 优化搜索速…

Selenium-用这个框架自动化任何你想做的事情!

Chrome DevTools 简介 Chrome DevTools 是一组直接内置在基于 Chromium 的浏览器&#xff08;如 Chrome、Opera 和 Microsoft Edge&#xff09;中的工具&#xff0c;用于帮助开发人员调试和研究网站。 借助 Chrome DevTools&#xff0c;开发人员可以更深入地访问网站&#xf…

职责链模式-请求的链式处理

在实际开发中&#xff0c;对于一个请求我们要经过层层过滤&#xff1a;身份验证 -> 权限验证 -> 实际业务处理。请求沿着一个链在传递&#xff0c;每一层都可以处理该请求。而“职责链模式”就是专门用于处理这种请求链式传递的模式。 1 职责链模式概述 避免将请求发送…

Kubernetes系列

文章目录 1 详解docker,踏入容器大门1.1 引言1.2 初始docker1.3 docker安装1.4 docker 卸载1.5 docker 核心概念和底层原理1.5.1 核心概念1.5.2 docker底层原理 1.6 细说docker镜像1.6.1 镜像的常用命令 1.7 docker 容器1.8 docker 容器数据卷1.8.1 直接命令添加1.8.2 Dockerfi…

2023-07-31 C语言根据错误号打印详细的错误信息perror(““) 或者strerror(errno)

一、C 语言可以使用perror("perror output"); 或 strerror(errno)打印详细的错误信息。 二、需要的头文件#include <errno.h>。 三、实例测试&#xff0c;这里我让open一个linux 底层杂项设备失败的情况&#xff0c;返回的是一个负数&#xff0c;强制返回-EN…

【shell】获取ping的时延数据并分析网络情况

网络情况经常让我们头疼&#xff0c;每次都需要手动在终端ping太麻烦了&#xff0c;不如写个脚本ping并将数据带上时间戳存入文件&#xff0c;然后也可以分析哪个时间段网络比较差。 创建一个demo.sh文件&#xff1a; #!/bin/bash # 清理日志 net_path"./network/"…

性能测试必备监控技能windows篇

前言 在手头没有专门的第三方监控时&#xff0c;该怎么监控服务指标呢&#xff1f;本篇就windows下监控进行分享&#xff0c;也是我们在进行性能测试时&#xff0c;必须掌握的。下面我们就windows下常用的三种监视工具进行说明&#xff1a; 任务管理器 资源监视器 性能监视器…

AD21 PCB设计的高级应用(一)BGA的扇出方式

&#xff08;一&#xff09;BGA的扇出方式 1.软件自动扇出方式2.手工扇出方式3.多种规格BGA的出现方式 1.软件自动扇出方式 在进行PCB设计时,常会遇到 BGA类型的封装&#xff0c;此类封装需要扇出用于后期的布线。BGA 扇出与否的比对如图所示。 (1)在进行利用软件自动扇出 BGA…

移动端适配布局rem和vw

在日益发展的移动互联网时代&#xff0c;作为前端开发者&#xff0c;我们必须了解和掌握各种移动端显示效果的适配技术。在众多适配方案中&#xff0c;使用rem和vw进行布局是当前最为流行和普遍使用的两种技术。通过合理运用这两种技术&#xff0c;我们可以让我们的网页在不同尺…

HCIP——前期综合实验

前期综合实验 一、实验拓扑二、实验要求三、实验思路四、实验步骤1、配置接口IP地址2、交换机配置划分vlan10以及vlan203、总部分部&#xff0c;骨干网配置OSPF分部总部骨干网 4、配置BGP建立邻居关系总部骨干网分部 5、发布用户网段6、将下一跳改为本地7、允许AS重复8、重发布…

揭秘!头条百科词条创建全过程及技巧解析

随着互联网时代的到来&#xff0c;人们获取信息的方式越来越便捷。作为国内领先的信息平台&#xff0c;头条百科成为了很多人查阅知识的首选。然而&#xff0c;如何在头条上创建百科词条&#xff0c;让更多人了解和熟知自己呢&#xff1f;本文伯乐网络传媒将为您揭开这个谜团&a…

何恺明把神经网络做深了,谷歌把神经网络的入口拉大了,又深又大,才成为今天的大模型

openai chatgpt 相关_个人渣记录仅为自己搜索用的博客-CSDN博客 大神回归学界&#xff1a;何恺明宣布加入 MIT 如今大模型都在使用的 transformer 的编码器和解码器&#xff0c;里面都有源自 ResNet 的残差链接。 「在 ResNet 之后就可以有效地训练超过百层的深度神经网络&…

Windows 实例如何开放端口

矩池云 Windows 实例相比于 Linux 实例&#xff0c;除了在租用机器的时候自定义端口外&#xff0c;还需要在 Windows防火墙中添加入口规则。接下来将教大家如何设置 Windows 防火墙&#xff0c;启用端口。 租用成功后通过 RDP 链接连接服务器&#xff0c;然后搜索防火墙&#x…

uniapp使用getStorage对属性赋值无效

1正常set(get)storage都是可以正常使用的 2.但对属性进行赋值的时候&#xff0c;却发现this.name并没有发生变化 3. 在里面打印this发现&#xff0c;在set*getStorage中并不能拿到this. 4.优化代码 这样就可以给this.name成功赋值