票据作为一种时效很敏感的数据,其过期策略的设计对其功能性和性能影响很大。本文将深度解析票据淘汰与过期策略,并基于此探究数据淘汰策略的设计,让我们一起走进企业级中央认证中心CAS的源码,分析其设计的巧妙之处。
文章重点分析源码的过程,不想看分析过程可以直接跳到总结处看结论!!!
文章目录
- A.相关阅读
- B.涉及源码作用及位置介绍
- C.默认票据淘汰策略深入解析
- 1.创建票据入口跟踪-过期策略从何而来
- 2.默认Service的淘汰策略
- 3.Service没有配置淘汰策略时如何构造
- 3.1 TicketGrantingTicketExpirationPolicyBuilder
- 3.2 解析参数构造存储策略
- 4.何时触发淘汰
- 4.1 getTicket时淘汰-延迟淘汰
- 4.2 定时清理
- D.总结
- 参考
A.相关阅读
- 【CAS6.6源码解析】在IDEA中调试可插拔的supprot模块
- 【CAS6.6源码解析】调试Rest API接口
- 【CAS6.6源码解析】深入解析TGT和ST的唯一ID是怎样生成的-探究ID生成器的设计
- 【CAS6.6源码解析】深度解析默认票据存储策略及其拓展支持-探究存储策略的设计
B.涉及源码作用及位置介绍
票据淘汰策略相关的代码位于cas-server-core-tickets-api
模块的ticket.expiration
包下。
C.默认票据淘汰策略深入解析
我们先来看TGT的淘汰策略,ST的设计与TGT一致,但默认参数差异较大。
1.创建票据入口跟踪-过期策略从何而来
在DefaultTicketGrantingTicketFactory
的produceTicket
方法中可以看到,
在创建TGT票据之前,会先通过service去取到相应的淘汰策略。
继续看getTicketGrantingTicketExpirationPolicy
函数:
这里直接获取了registeredService
参数中的存储策略。registeredService
是在Services Management中注册的服务,也就是预先配置好的服务配置。
从这里我们大致可以看出,票据的淘汰策略和registeredService
是强关联的。
2.默认Service的淘汰策略
接下来我们调试下源码,看一下系统里默认配置的Service,使用的是何种淘汰策略。
从这里我们可以看出,系统默认配置的Service中没有配置淘汰策略
3.Service没有配置淘汰策略时如何构造
从上面源码可以看到,当Service没有配置淘汰策略时,会通过ticketGrantingTicketExpirationPolicy.buildTicketExpirationPolicy();
去构建一个票据淘汰策略。
3.1 TicketGrantingTicketExpirationPolicyBuilder
我们看一下配置类中这个ticketGrantingTicketExpirationPolicy
的实现类,是TicketGrantingTicketExpirationPolicyBuilder
,并且将配置的参数传递给了它。
我们再来分析一下TicketGrantingTicketExpirationPolicyBuilder
的buildTicketExpirationPolicy()
方法的实现逻辑:
这里的逻辑很简单,就是判断是不是开启了RememberMe,进入不同的解析配置的方法。
3.2 解析参数构造存储策略
看toTicketGrantingTicketExpirationPolicy
方法,大致浏览一下,发现主要是在依据配置判断返回何种淘汰策略实例。
我们看看是怎么解析这些配置的。
第一步:如果票据是设置的永不过期,那么将返回一个永不过期的实例。
如何判断永不过期,核心代码如下:(即-1、空值、INFINITE)
public static boolean isInfinitelyDurable(final String value) {
return "-1".equalsIgnoreCase(value) || !StringUtils.hasText(value) || "INFINITE".equalsIgnoreCase(value);
}
第二步:如果票据是立即过期的,那么将返回一个立即过期的实例。
如何判断立即过期,核心代码如下:(即0、NEVER、空值)
public static boolean isNeverDurable(final String value) {
return "0".equalsIgnoreCase(value) || "NEVER".equalsIgnoreCase(value) || !StringUtils.hasText(value);
}
注意,空值同时会被判为永不过期何立即过期,但是在或逻辑里面的顺序不同,所以可以通过控制工具方法的执行顺序来达到目的。
第三步:如果设置了超时配置(超过一段时间没有使用,即过期),那么优先返回超时配置。系统默认没有配置超时配置。
第四步:如果配置了门限超时配置(每多少毫秒只能使用一次,防止资源浪费),那么优先返回此配置,系统默认没有配置此配置。
第五步:如果配置了基于票据创建时间的硬超时配置,那么优先返回此配置,系统默认没有配置此配置。
如果上述步骤都未成功返回,那么获取经典配置,构造一个TicketGrantingTicketExpirationPolicy
对象。
在配置文件中这两个参数的含义是:
maxTimePrimarySeconds
:票据最大存活时间。默认是8小时。ttlPrimarySeconds
:超过多少时间后,票据不活跃将会被销毁。默认是2小时。
从这里我们可以看出,系统默认配置的TGT淘汰策略是经典策略(最多存活8小时,超过2小时未活跃则销毁),并可以拓展配置永不过期、立即过期、超时、门限超硬超时配置。
从上面可以总结,如果service没有配置淘汰策略,那么会从配置文件中取到默认的配置,是一个TicketGrantingTicketExpirationPolicy
对象,然后传递给TicketGrantingTicketImpl
用于实例化。
到这里,TGT的淘汰策略从而而来已经完全弄清楚了。
4.何时触发淘汰
在一个TGT对象创建的时候,淘汰策略已经被确定,接下来看是什么时候触发淘汰的。
4.1 getTicket时淘汰-延迟淘汰
在从TicketRegistry获取票据时,如果判断已经过期,那么将会删除这个票据,这是一种延迟淘汰机制。
那么是怎么判断票据是否过期的呢?核心代码如下:
@Override
public boolean isExpired() {
return this.expirationPolicy.isExpired(this) || isExpiredInternal();
}
如果票据淘汰策略判断过期了或者内部判断过期了,就会返回已过期。其中,内部过期就是用户强制手动过期。
再细看一下淘汰策略里面是如何判断过期的,很简单,拿票据创建时间、上次使用时间和服务器系统时间进行一下比对即可。
4.2 定时清理
CAS默认会通过一个定时任务去清理一次所有过期的票据。其顶级接口如下:
看一下容器中注入的是何种实现类:
从这里可以发现,如果配置里配置了开启定时清理,就会注入一个DefaultTicketRegistryCleaner
实例,如果没有开启,将不会注入可用的清理实例。系统默认是开启了票据定时清理的。
往下看,可以发现定时任务的注册:
系统默认延迟30s开启定时清理任务,每120s执行一次
看一下DefaultTicketRegistryCleaner
的逻辑,其实也很简单:
首先获取到所有已经过期的票据,然后调用清理方法进行清理。
如果清理的是TGT票据,同时会进行登出操作。
到这里,基本上淘汰策略的获取与淘汰操作的具体执行都弄清楚了,上述所讲都是基于默认配置,如果进行了其它拓展配置,新情况可能会有所出入。
D.总结
从CAS的默认淘汰策略的流程来说:
- 淘汰策略优先从RegisteredService中取,如果相应的注册服务没有配置淘汰策略,才会使用配置文件中的淘汰策略。票据的淘汰策略和registeredService是强关联的。 系统默认注册的服务HTTPS and IMAPS,并没有配置淘汰策略。
- 系统默认配置的TGT淘汰策略是经典策略(最多存活8小时,超过2小时未活跃则销毁),并可以拓展配置永不过期、立即过期、超时、门限超硬超时配置。
- CAS对过期票据默认有两种淘汰操作:
- 在获取票据时(getTicket)如果判断过期,则会立即删除。
- 默认开启一个延迟30s启动,每隔120s执行一次的定时清理过期票据任务,如果清理的是TGT票据,还会同时触发登出操作。
从CAS的淘汰策略的设计来说:
- 设计了淘汰策略,淘汰策略构造器,定时清理过期票据,将淘汰策略和服务强关联,并且有默认的淘汰策略配置,可拓展性极强。
- 用于淘汰相关的参数,全部从配置文件中取,只需要简单的配置,即可完成强大的功能。
参考
截止2023-08-01为止,还没有专门分析CAS6源码的文章可检索,本文只参考了CAS6.6的源代码,所有分析过程均经过动态调试验证。
ATFWUS 2023-08-01