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

chatgpt/2023/9/27 15:48:38

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);}@Overridepublic void addTicketInternal(final Ticket ticket) throws Exception {val encTicket = encodeTicket(ticket);LOGGER.debug("Putting ticket [{}] in registry.", ticket.getId());getMapInstance().put(encTicket.getId(), encTicket);}@Overridepublic 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;}@Overridepublic long deleteSingleTicket(final String ticketId) {val encTicketId = encodeTicketId(ticketId);return !StringUtils.isBlank(encTicketId) && getMapInstance().remove(encTicketId) != null ? 1 : 0;}@Overridepublic long deleteAll() {val size = getMapInstance().size();getMapInstance().clear();return size;}@Overridepublic Collection<? extends Ticket> getTickets() {return decodeTickets(getMapInstance().values());}@Overridepublic 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.exyb.cn/news/show-5313639.html

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

GULI PART.1

文章目录 1、尚硅谷-谷粒学院1.1、系统功能模块介绍1.2、系统开发方式 2、Mybatis-Plus2.1、什么是 MyBatis&#xff1f;2.2、什么是Mybatis-Plus&#xff1f;2.3、Mybatis-plus 的特性2.4、支持的数据库 3、Mybatis-Plus入门3.1、创建表和数据3.2、创建SpringBoot工程3.3、安装…

vue函数防抖

防抖的概念  防抖又叫为函数防抖&#xff08;debounce&#xff09;&#xff1a;指触发事件后&#xff0c;在 n 秒内函数只能执行一次&#xff0c;如果触发事件后在 n 秒内又触发了事件&#xff0c;则会重新计算函数延执行时间。  前端开发中&#xff0c;常见的事件如&#…

【ChatGPT辅助学Rust | 基础系列 | Hello, Rust】编写并运行第一个Rust程序

文章目录 前言一&#xff0c;创建项目二&#xff0c;两种编译方式1. 使用rustc编译器编译2. 使用Cargo编译 总结 前言 在开始学习任何一门新的编程语言时&#xff0c;都会从编写一个简单的 “Hello, World!” 程序开始。在这一章节中&#xff0c;将会介绍如何在Rust中编写并运…

轻松解决宝塔面板设置了授权IP访问,但是IP变动导致访问不了面板

为了宝塔面板的安全与隐蔽性&#xff0c;我们很多站长会设置授权IP&#xff0c;授权IP的作用是&#xff1a;设置访问授权IP,多个请使用逗号(,)隔开;注意&#xff1a;一旦设置授权IP,只有指定IP的电脑能访问面板! 但是很多站长不是通过专用的虚拟通道访问&#xff0c;用的都是宽…

智慧展馆展厅人员定位系统解决方案:提升参观体验与管理效率

随着数字化时代的到来&#xff0c;展馆和展厅逐渐成为人们了解文化、艺术、科技等领域的重要窗口。 然而&#xff0c;传统的展馆和展厅存在着一些问题&#xff0c;例如参观者迷路、信息获取不及时、管理效率低下等。 为了提升参观体验和管理效率&#xff0c;研发智慧展馆展厅…

红队攻防 | 解决HW被疯狂封IP姿势~(附下载)

本项目其实就是个简单的代理服务器&#xff0c;经过小小的修改。加了个代理池进来。渗透、爬虫的时候很容易就会把自己ip给ban了&#xff0c;所以就需要ip代理池了。 ProxyPool 爬虫代理IP池项目,主要功能为定时采集网上发布的免费代理验证入库&#xff0c;定时验证入库的代理…

goctl template一定制化服务配置生成

官网介绍&#xff1a; 模板&#xff08;Template&#xff09;是数据驱动生成的基础&#xff0c;所有的代码&#xff08;rest api、rpc、model、docker、kube&#xff09;生成都会依赖模板&#xff0c; 默认情况下&#xff0c;模板生成器会选择内存中的模板进行生成&#xff0c…

js中复选框的全选和捕捉回车键

复选框的全选 可以实现当点击全选按钮时&#xff0c;所有按钮都会被选中&#xff0c;当下面所有分按钮都被选中后&#xff0c;全选按钮自动亮起。 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></hea…
推荐文章