深入理解CAS - CAS票据


CAS架构图

如图所示,CAS认证过程中最重要的组件之一就是票据了。

CAS的两个核心票据

TGT:Ticket Grangting Ticket,即票据的票据,给CAS client签发服务票据的票据。只有持有TGT的用户才能证明自己登录过,进行下一步的授权操作。

ST:Service Ticket,即服务票据,是CAS Server通过TGT生成的签发给Cas Client的票据。拥有ST的Cas Client才能证明CAS Server授权过,才能进行下一步子系统的创建Session,访问等操作。

CAS票据实现原理

Jbone项目中使用了CAS的JPA方式将票据信息保存到数据库中,当然CAS还支持其他的持久化方式来保存票据,如Redis、monggo、内存等,本文就以Jbone项目的实现作为依据。

票据生命周期

TGT票据生命周期

  1. 首先用户登录CAS成功后,CAS会自动创建TGT,并下发到浏览器。
  2. 然后用户访问子系统的时候会发起单点登录的处理流程,CAS通过TGT生成ST和Service的对应关系,更新到TGT对象信息里。
  3. 用户访问新的子系统,都会更新TGT的信息。
  4. 最后用户注销时,CAS删除TGT记录。

ST生命周期

  1. 用户第一次访问子系统,会302重定向到CAS系统
  2. 用户输入用户名和密码,登录成功,创建TGT。
  3. 然后CAS系统会通过TGT生成当前Service的ST并重定向到第一步访问的子系统
  4. 子系统接收到ST,会向Cas Server发起ST的校验
  5. 校验之后,Cas Server会根据ST的过期策略判定ST是否过期,如果过期了就销毁ST。

票据存储结构

Jbone使用的是JPA,所以都是面向对象的存储,理解实体的关系也就理解了票据是如何存储的了。

TicketGrantingTicket

TicketGrantingTicket就是TGT的票据实体对象,保存了TGT的基本信息,以及授权的Service信息等。下面是注销时查询出的TicketGrantingTicket对象:

几个比较重要的属性:

  • id: TGT开头,即TGT的唯一值,在单点登录和注销的过程中都是携带的它。
  • services:即授权过的Service和对应的ST信息。在注销的时候会获取到Service并携带ST回调进行单点登出。
  • expirationPolicy:过期策略,Jbone使用的默认策略,28800秒后过期。

ServiceTicket

ServiceTicket是ST的实体对象,保存了ST的基本信息,以及对应的Service、TGT信息等。下面是验证过程中的ServiceTicket对象:

几个比较重要的属性:

  • id:ST开头,即ST的唯一值,通过TGT生成,在系统认证以及注销过程中都是携带的它。
  • ticketGrantingTIcket:即ST对应的TGT对象。
  • service:ST对应的Service对象
  • expirationPolicy:ST过期策略,这里使用了两种策略:使用次数和超时时间。验证后满足任意一种都会失效。

ExpirationPolicy

TicketGrantingTicket和ServiceTicket的父接口Ticket里有个共同的属性expirationPolicy。即票据的过期策略。下面分别还有TicketGrantingTicketExpirationPolicy和ServiceTicketExpirationPolicy的不同实现。

TGT的过期策略

    @Override
    public boolean isExpired(final TicketState ticketState) {
        final ZonedDateTime currentSystemTime = getCurrentSystemTime();
        final ZonedDateTime creationTime = ticketState.getCreationTime();
        final ZonedDateTime lastTimeUsed = ticketState.getLastTimeUsed();

        // Ticket has been used, check maxTimeToLive (hard window)
        ZonedDateTime expirationTime = creationTime.plus(this.maxTimeToLiveInSeconds, ChronoUnit.SECONDS);
        if (currentSystemTime.isAfter(expirationTime)) {
            LOGGER.debug("Ticket is expired because the time since creation [{}] is greater than current system time", expirationTime, currentSystemTime);
            return true;
        }

        expirationTime = lastTimeUsed.plus(this.timeToKillInSeconds, ChronoUnit.SECONDS);
        if (currentSystemTime.isAfter(expirationTime)) {
            LOGGER.debug("Ticket is expired because the time since last use is greater than timeToKillInSeconds");
            return true;
        }

        return false;
    }

TGT有两种策略:

  1. 一种是创建时间大于特定值(maxTimeToLiveInSeconds)即过期;
  2. 另一种是特定时间内(timeToKillInSeconds)没有使用过即过期;

ST的过期策略

    @Override
    public boolean isExpired(final TicketState ticketState) {
        if (ticketState == null) {
            LOGGER.debug("Ticket state is null for [{}]. Ticket has expired.", this.getClass().getSimpleName());
            return true;
        }
        final long countUses = ticketState.getCountOfUses();
        if (countUses >= this.numberOfUses) {
            LOGGER.debug("Ticket usage count [{}] is greater than or equal to [{}]. Ticket has expired", countUses, this.numberOfUses);
            return true;
        }

        final ZonedDateTime systemTime = getCurrentSystemTime();
        final ZonedDateTime lastTimeUsed = ticketState.getLastTimeUsed();
        final ZonedDateTime expirationTime = lastTimeUsed.plus(this.timeToKillInSeconds, ChronoUnit.SECONDS);

        if (systemTime.isAfter(expirationTime)) {
            LOGGER.debug("Ticket has expired because the difference between current time [{}] and ticket time [{}] is greater than or equal to [{}].",
                    systemTime, lastTimeUsed, this.timeToKillInSeconds);
            return true;
        }
        return false;
    }

ST也有两种过期策略:

  1. 一种是特定时间内(timeToKillInSeconds)没有使用过即过期;
  2. 一种是特定超过特定使用次数(countOfUses)即过期;

 

原创文章,转载请注明出处:转载自小马过河 - 深入理解CAS - CAS票据


Jbone

Spring Cloud实战项目jbone正在开发中, jbone功能包括服务管理、单点登录、系统管理平台、内容管理平台、电商平台、支付平台、工作流平台等子系统。欢迎关注!

GitHub 码云
马军伟
关于作者 马军伟
写的不错,支持一下

先给自己定个小目标,日更一新。