CAS项目介绍(三)

本文深入探讨了CAS系统的票务机制,详细介绍了Ticket接口及其核心实现类TicketRegistry的功能,特别是JpaTicketRegistry的具体实现,并解析了Ticket代理类的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CAS的票据类的设计


cas的核心服务内容是登陆授权的赋予和验证,而其中在服务过程中每一次操作都用到了票据ticket的验证。说白了就是ticket一系列的增删改查操作。

我们知道cas的ticket主要分成两种TGT(ticketgrantticket)和ST(serviceticket)。TGT负责用户认证而ST负责用户授权。但他们的一些基础行为都很类似所以,他们拥有同一个接口(Ticket)。

public interface Ticket extends Serializable {

    /**
     * Method to retrieve the id.
     * 
     * @return the id
     */
    String getId();

    /**
     * Determines if the ticket is expired. Most common implementations might
     * collaborate with <i>ExpirationPolicy </i> strategy.
     * 
     * @see org.jasig.cas.ticket.ExpirationPolicy
     */
    boolean isExpired();

    /**
     * Method to retrive the TicketGrantingTicket that granted this ticket.
     * 
     * @return the ticket or null if it has no parent
     */
    TicketGrantingTicket getGrantingTicket();

    /**
     * Method to return the time the Ticket was created.
     * 
     * @return the time the ticket was created.
     */
    long getCreationTime();
    
    /**
     * Returns the number of times this ticket was used.
     * @return
     */
    int getCountOfUses();
}
ticket在这个看作是数据对象,而对于这些ticket进行操作的工作交给了TicketRegistry的实现类,我们先来看一些他的借口。

public interface TicketRegistry {

    /**
     * Add a ticket to the registry. Ticket storage is based on the ticket id.
     * 
     * @param ticket The ticket we wish to add to the cache.
     */
    void addTicket(Ticket ticket);

    /**
     * Retrieve a ticket from the registry. If the ticket retrieved does not
     * match the expected class, an InvalidTicketException is thrown.
     * 
     * @param ticketId the id of the ticket we wish to retrieve.
     * @param clazz The expected class of the ticket we wish to retrieve.
     * @return the requested ticket.
     * @throws InvalidTicketClassException if the ticket does not match the
     * class provided.
     */
    Ticket getTicket(String ticketId, Class<? extends Ticket> clazz);

    /**
     * Retrieve a ticket from the registry.
     * 
     * @param ticketId the id of the ticket we wish to retrieve
     * @return the requested ticket.
     */
    Ticket getTicket(String ticketId);

    /**
     * Remove a specific ticket from the registry.
     * 
     * @param ticketId The id of the ticket to delete.
     * @return true if the ticket was removed and false if the ticket did not
     * exist.
     */
    boolean deleteTicket(String ticketId);

    /**
     * Retrieve all tickets from the registry.
     * 
     * @return collection of tickets currently stored in the registry. Tickets
     * might or might not be valid i.e. expired.
     */
    Collection<Ticket> getTickets();
}

方法很简单,基本上都是根据ticketid主键对ticket实体进行增删。

在来看一下他的实现类JpaTicketRegistry

public final class JpaTicketRegistry extends AbstractDistributedTicketRegistry implements TicketRegistryState {
    
    @NotNull
    @PersistenceContext
    private EntityManager entityManager;
        
    @NotNull
    private String ticketGrantingTicketPrefix = "TGT";


    protected void updateTicket(final Ticket ticket) {
        entityManager.merge(ticket);
        log.debug("Updated ticket [{}].", ticket);
    }

    @Transactional(readOnly = false)
    public void addTicket(final Ticket ticket) {
        entityManager.persist(ticket);
        log.debug("Added ticket [{}] to registry.", ticket);
    }

    @Transactional(readOnly = false)
    public boolean deleteTicket(final String ticketId) {
        final Ticket ticket = getRawTicket(ticketId);
        
        if (ticket == null) {
            return false;
        }
        
        if (ticket instanceof ServiceTicket) {
            removeTicket(ticket);
            log.debug("Deleted ticket [{}] from the registry.", ticket);
            return true;
        }
        
        deleteTicketAndChildren(ticket);
        log.debug("Deleted ticket [{}] and its children from the registry.", ticket);
        return true;
    }
    
    private void deleteTicketAndChildren(final Ticket ticket) {
        final List<TicketGrantingTicketImpl> ticketGrantingTicketImpls = entityManager
            .createQuery("select t from TicketGrantingTicketImpl t where t.ticketGrantingTicket.id = :id", TicketGrantingTicketImpl.class)
            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
            .setParameter("id", ticket.getId())
            .getResultList();
        final List<ServiceTicketImpl> serviceTicketImpls = entityManager
	        .createQuery("select s from ServiceTicketImpl s where s.ticketGrantingTicket.id = :id", ServiceTicketImpl.class)
	        .setParameter("id", ticket.getId())
	        .getResultList();
        
        for (final ServiceTicketImpl s : serviceTicketImpls) {
            removeTicket(s);
        }
        
        for (final TicketGrantingTicketImpl t : ticketGrantingTicketImpls) {
            deleteTicketAndChildren(t);
        }
        
        removeTicket(ticket);
    }
    
    private void removeTicket(final Ticket ticket) {
        try {
            if (log.isDebugEnabled()) {
                final Date creationDate = new Date(ticket.getCreationTime());
                log.debug("Removing Ticket [{}] created: {}", ticket, creationDate.toString());
             }
            entityManager.remove(ticket);
        } catch (final Exception e) {
            log.error("Error removing {} from registry.", ticket, e);
        }
    }
    
    @Transactional(readOnly=true)
    public Ticket getTicket(final String ticketId) {
        return getProxiedTicketInstance(getRawTicket(ticketId));
    }
    
    private Ticket getRawTicket(final String ticketId) {
        try {
            if (ticketId.startsWith(this.ticketGrantingTicketPrefix)) {
                return entityManager.find(TicketGrantingTicketImpl.class, ticketId, LockModeType.PESSIMISTIC_WRITE);
            }
            
            return entityManager.find(ServiceTicketImpl.class, ticketId);
        } catch (final Exception e) {
            log.error("Error getting ticket {} from registry.", ticketId, e);
        }
        return null;
    }

    @Transactional(readOnly=true)
    public Collection<Ticket> getTickets() {
        final List<TicketGrantingTicketImpl> tgts = entityManager
            .createQuery("select t from TicketGrantingTicketImpl t", TicketGrantingTicketImpl.class)
            .getResultList();
        final List<ServiceTicketImpl> sts = entityManager
            .createQuery("select s from ServiceTicketImpl s", ServiceTicketImpl.class)
            .getResultList();
        
        final List<Ticket> tickets = new ArrayList<Ticket>();
        tickets.addAll(tgts);
        tickets.addAll(sts);
        
        return tickets;
    }
    
    public void setTicketGrantingTicketPrefix(final String ticketGrantingTicketPrefix) {
        this.ticketGrantingTicketPrefix = ticketGrantingTicketPrefix;
    }

    @Override
    protected boolean needsCallback() {
        return false;
    }

    @Transactional(readOnly=true)
    public int sessionCount() {
        return countToInt(entityManager.createQuery("select count(t) from TicketGrantingTicketImpl t").getSingleResult());
    }

    @Transactional(readOnly=true)
    public int serviceTicketCount() {
        return countToInt(entityManager.createQuery("select count(t) from ServiceTicketImpl t").getSingleResult());
    }

    private int countToInt(final Object result) {
        final int intval;
        if (result instanceof Long) {
            intval = ((Long) result).intValue();
        } else if (result instanceof Integer) {
            intval = (Integer) result;
        } else {
            // Must be a Number of some kind
            intval = ((Number) result).intValue();
        }
        return intval;
    }
}

实现类就有些复杂了,不光是具体实现了借口的一些基本方法,而且也对TGT和ST进行一系列的相关操作。

在类中我们看到updateticket和addticket方法比较简单,基本上只是调用jpa的api,将ticket入口。而getticket方法则不然。

在这个类中一个有两个获得ticket的方法,一是getRawTicket获得的是ticket实体类可以理解为数据库的源数据,二是getTicket他返回的是ticket的一个代理类。

这个代理类封装了一些ticket的方法。

public Ticket getTicket(final String ticketId) {
        return getProxiedTicketInstance(getRawTicket(ticketId));
    }
getProxiedTicketInstance是JpaTicketRegistry的父类方法

protected final Ticket getProxiedTicketInstance(final Ticket ticket) {
        if (ticket == null) {
            return null;
        }

        if (ticket instanceof TicketGrantingTicket) {
            return new TicketGrantingTicketDelegator(this, (TicketGrantingTicket) ticket, needsCallback());
        }

        return new ServiceTicketDelegator(this, (ServiceTicket) ticket, needsCallback());
    }
这里对ticket的类型进行判断,如果是TGT类型的则返回TGT的代理,如果是ST类型的则返回ST的代理。

先看一下TGT的代理类,它就被定义在这个父类里作为一个内部类。

private static final class TicketGrantingTicketDelegator extends TicketDelegator<TicketGrantingTicket> implements TicketGrantingTicket {

        protected TicketGrantingTicketDelegator(final AbstractDistributedTicketRegistry ticketRegistry, final TicketGrantingTicket ticketGrantingTicket, final boolean callback) {
            super(ticketRegistry, ticketGrantingTicket, callback);
        }

        public Authentication getAuthentication() {
            return getTicket().getAuthentication();
        }

        public ServiceTicket grantServiceTicket(final String id, final Service service, final ExpirationPolicy expirationPolicy, final boolean credentialsProvided) {
            final ServiceTicket t = this.getTicket().grantServiceTicket(id, service, expirationPolicy, credentialsProvided);
            updateTicket();
            return t;
        }

        public void expire() {
            this.getTicket().expire();
            updateTicket();
        }

        public boolean isRoot() {
            return getTicket().isRoot();
        }

        public List<Authentication> getChainedAuthentications() {
            return getTicket().getChainedAuthentications();
        }
    }

我们看到在这个类中增加了对ticket内部数据的一些操作。他们的具体实现方法都写在了TicketGrantingTicketImpl类中

public synchronized ServiceTicket grantServiceTicket(final String id,
        final Service service, final ExpirationPolicy expirationPolicy,
        final boolean credentialsProvided) {
        final ServiceTicket serviceTicket = new ServiceTicketImpl(id, this,
            service, this.getCountOfUses() == 0 || credentialsProvided,
            expirationPolicy);

        updateState();
        
        final List<Authentication> authentications = getChainedAuthentications();
        service.setPrincipal(authentications.get(authentications.size()-1).getPrincipal());
        
        this.services.put(id, service);

        return serviceTicket;
    }
    
    private void logOutOfServices() {
        for (final Entry<String, Service> entry : this.services.entrySet()) {

            if (!entry.getValue().logOutOfService(entry.getKey())) {
                LOG.warn("Logout message not sent to [" + entry.getValue().getId() + "]; Continuing processing...");   
            }
        }
    }

    public boolean isRoot() {
        return this.getGrantingTicket() == null;
    }

    public synchronized void expire() {
        this.expired = true;
        logOutOfServices();
    }

    public boolean isExpiredInternal() {
        return this.expired;
    }

    public List<Authentication> getChainedAuthentications() {
        final List<Authentication> list = new ArrayList<Authentication>();

        if (this.getGrantingTicket() == null) {
            list.add(this.getAuthentication());
            return Collections.unmodifiableList(list);
        }

        list.add(this.getAuthentication());
        list.addAll(this.getGrantingTicket().getChainedAuthentications());

        return Collections.unmodifiableList(list);
    }
ST代理类的设计也非常类似。

private static final class ServiceTicketDelegator extends TicketDelegator<ServiceTicket> implements ServiceTicket {

        protected ServiceTicketDelegator(final AbstractDistributedTicketRegistry ticketRegistry, final ServiceTicket serviceTicket, final boolean callback) {
            super(ticketRegistry, serviceTicket, callback);
        }


        public Service getService() {
            return getTicket().getService();
        }

        public boolean isFromNewLogin() {
            return getTicket().isFromNewLogin();
        }

        public boolean isValidFor(final Service service) {
            final boolean b = this.getTicket().isValidFor(service);
            updateTicket();
            return b;
        }

        public TicketGrantingTicket grantTicketGrantingTicket(final String id, final Authentication authentication, final ExpirationPolicy expirationPolicy) {
            final TicketGrantingTicket t = this.getTicket().grantTicketGrantingTicket(id, authentication, expirationPolicy);
            updateTicket();
            return t;
        }
    }

总结:cas的所有ticket都基于同一个借口,而ticket附加的一些具体行为都写分别写在了他们 实现类中,比如tgt与ST的互相获取(我们知道tgt对应着多个st)。

而这些ticket的基本行为,cas并没有直接把他们暴露出去。而是增加了一个代理类,这样其他类调用JpaTicketRegistry时在增加删除时无影响而在getticekt时,

获得到的ticket并不是源ticket,这个对象已经拥有了一些额外方法。使用时无需关心数据是如何联立的,删除某个ticket而又需要删除多少关联数据。这些人物都将

在代理类中进行调用。而在代码中,我们看到代理类的一些方法都调用了updateTicket方法,此方法就是更新ticket数据。这个方法是private的,说明获取的ticket不允许被

显示的update,这些工作只允许出现在某些特定的业务方法中,这也完全体现出了代理模式的优点。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值