cas 入门之十三:ticket 存储方案之简介

来源:互联网 发布:淘宝html代码生成器 编辑:程序博客网 时间:2024/04/27 23:09
    cas认证过程中涉及两类ticket,一类是TGT,一类是ST,在默认的情况下,这些ticket是存储在服务端的内存中。为了更好的提高服务器的性能,以及cas服务器集群化,cas系统又提供了几种ticket的存储方案。cas一共提供了如下几种存储方案:

ConcurrentHashMap ticket存储,这个也是默认存储方案;

jpa ticket存储;
ehcache ticket存储;
memcache ticket 存储;
jbosstree ticket 存储;

     在用户量不是很大的情况下,用不到cas的集群(或者HA),所以也就不会用到cas ticket的其他存储方案。但是了解cas ticket的这几种存储方案,为日后更大的系统集成作准备。后面会对每一种ticket存储逐一剖析。(目前对于jbosstree ticket 存储还没有进行过测试)。


ticket 存储方案之ConcurrentHashMap ticket存储

cas 默认ticket存储放在ConcurrentHashMap,因为这种存储方式对于单个cas server服务器,那是再合适不过了,
当然对于它的性能调整,也就是对于ConcurrentHashMap的性能调整,
默认情况下配置是:

<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry" />

默认ticketRegistry涉及到三个参数:

initialCapacity - 初始容量。该实现执行内部大小调整,以容纳这些元素。
loadFactor - 加载因子阈值,用来控制重新调整大小。在每 bin 中的平均元素数大于此阈值时,可能要重新调整大小。
concurrencyLevel - 当前更新线程的估计数。该实现将执行内部大小调整,以尽量容纳这些线程。 
在默认情况下,initialCapacity=16,loadFactor=0.75,concurrencyLevel=16;这个也是ConcurrentHashMap的无参构造器的默认参数,
当有性能需求时,可以进行相应的参数配置:
<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry">
<constructor-arg index="0" value="1000" />
<constructor-arg index="1" value="1" />
<constructor-arg index="2" value="16" />
</bean>



cas 入门之十五:ticket 存储方案-jpa ticket存储

  jpa ticket存储,也就是可以让cas将ticket存储在后端的数据库,比如oracle,mysql等。这个功能可以让我们来构建高可用的cas 服务器集群。下面我会描述具体的配置,让cas如何将ticket存入后端oracle数据库,在实际生产应用中我也是存入oracle数据库的。其实存在哪个数据库都一样,相互转换很容易,只需要更改一下数据源及数据库驱动就可以了。cas/webapp/WEB-INF/spring-configuration/ticketRegistry.xml 找到这个文件,对于jpa ticket的配置,只需要更改这个文件。(对于cas spring配置文件,可参看cas入门之二spring配置文件)
步骤:
1.更换默认ticket存储器
  <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry" />
   替换为
   <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.JpaTicketRegistry" />
   并在ticketRegistry.xml文件中加入如下bean配置:
   <!--  让@PersistenceUnit and @PersistenceContext 自动注入 EntityManager/Factory 实例 -->

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="dataSource" ref="dataSource"/>
   <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
         <property name="generateDdl" value="true"/>
         <property name="showSql" value="true" />
      </bean>
   </property>
   <property name="jpaProperties">
   <props>
             <!-- 数据库驱动  -->
       <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
       <prop key="hibernate.hbm2ddl.auto">update</prop>
   </props>
</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 数据源配置略-->
<bean id="dataSource"  />

<bean id="cleanerLock"
            class="org.jasig.cas.ticket.registry.support.JpaLockingStrategy"
            p:uniqueId="${host.name}"
            p:applicationId="cas-ticket-registry-cleaner" />

这里主要说一下cleanerLock,它会在数据库是产生locks表:
Name            Type          Nullable Default Comments 
--------------- ------------- -------- ------- -------- 
APPLICATION_ID  VARCHAR2(255)                           
EXPIRATION_DATE DATE          Y                         
UNIQUE_ID       VARCHAR2(255) Y  
在集群环境,多个cas 服务器共享同一个数据库节点,当需要清理ticket时,会出现表争用,但是cas通过cleanerLock解决了这个问题,同时并不会影响应用的性能。

2.ticket清理
<bean id="ticketRegistryCleaner" class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
p:ticketRegistry-ref="ticketRegistry" />
更改为
<bean id="ticketRegistryCleaner"   class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
        p:ticketRegistry-ref="ticketRegistry"
        p:lock-ref="cleanerLock" />
其余的不变。
3.增加相应的jar
主要的jar:
hibernate-core
hibernate-entitymanager
hibernate-jpa-2.0-api
hibernate-commons-annotations
hibernate-validator
另外还有相应数据库的jar,在此不列举。到此关于jpa ticket配置更改完毕。

jpa ticket配置更改完成,重新部署cas,会在相应的数据库,生成5张表:
LOCKS 
RegisteredServiceImpl 
SERVICETICKET
TICKETGRANTINGTICKET 
rs_attributes
如果不能生成,这5张表在oracle的建表sql:
create table LOCKS
(
  APPLICATION_ID  VARCHAR2(255) not null,
  EXPIRATION_DATE DATE,
  UNIQUE_ID       VARCHAR2(255)
)
;
alter table LOCKS
  add primary key (APPLICATION_ID);
  
create table RS_ATTRIBUTES
(
  REGISTEREDSERVICEIMPL_ID NUMBER(19) not null,
  A_NAME                   VARCHAR2(255) not null,
  A_ID                     NUMBER(10) not null
)
;
alter table RS_ATTRIBUTES
  add primary key (REGISTEREDSERVICEIMPL_ID, A_ID);


create table RegisteredServiceImpl 
 (expression_type VARCHAR2(15) DEFAULT 'ant' not null,
  id number(19,0) not null,
 allowedToProxy char(1) not null, 
 anonymousAccess char(1) not null, 
 description varchar2(255), 
 enabled char(1) not null, 
 evaluation_order number(10,0) not null,
  ignoreAttributes char(1) not null,
 name varchar2(255), 
 serviceId varchar2(255), 
 ssoEnabled char(1) not null,
  theme varchar2(255), 
 username_attr varchar2(256),
  primary key (id)
  );
  
create table SERVICETICKET (
 ID varchar2(255) not null, 
 NUMBER_OF_TIMES_USED number(10,0),
 CREATION_TIME number(19,0), 
 EXPIRATION_POLICY blob not null, 
 LAST_TIME_USED number(19,0),
 PREVIOUS_LAST_TIME_USED number(19,0),
 FROM_NEW_LOGIN char(1) not null, 
 TICKET_ALREADY_GRANTED char(1) not null,
 SERVICE blob not null, 
 ticketGrantingTicket_ID varchar2(255),
 primary key (ID))
 ;
 
create table TICKETGRANTINGTICKET (
 ID varchar2(255) not null,
 NUMBER_OF_TIMES_USED number(10,0), 
CREATION_TIME number(19,0), 
EXPIRATION_POLICY blob not null,
 LAST_TIME_USED number(19,0), 
PREVIOUS_LAST_TIME_USED number(19,0), 
AUTHENTICATION blob not null,
 EXPIRED char(1) not null, 
SERVICES_GRANTED_ACCESS_TO blob not null, 
ticketGrantingTicket_ID varchar2(255), primary key (ID));


alter table SERVICETICKET add constraint FK7645ADE132A2C0E5 foreign key (ticketGrantingTicket_ID) 
 references TICKETGRANTINGTICKET;
 
 
alter table TICKETGRANTINGTICKET add constraint FKB4C4CDDE32A2C0E5 foreign key (ticketGrantingTicket_ID) 
references   TICKETGRANTINGTICKET;


alter table rs_attributes add constraint FK4322E153C595E1F foreign key (RegisteredServiceImpl_id) 
references RegisteredServiceImpl;
--下面两个索引是为了提高应用性能
CREATE INDEX ST_TGT_FK_I
ON SERVICETICKET (TICKETGRANTINGTICKET_ID)
COMPUTE STATISTICS;


CREATE INDEX TGT_TGT_FK_I
ON TICKETGRANTINGTICKET (TICKETGRANTINGTICKET_ID)
COMPUTE STATISTICS;



cas 入门之十六:ticket 存储方案之ehcache ticket存储

 我们利用ehcache缓存ticket,就是缓存tgt(ticket granting ticket),st(service ticket)。那么这两类ticket有什么不同呢?
根据cas的基本原理,我们知道tgt就是cas发放给用户的,并写入cookie中,用于访问下一个应用进行验证的,它的生合周期相比是非常长的;st是在认证过程中cas服务器发放给应用端的认证凭证,而后应用通过URLConnection与cas服务端进行再交互认证其合法性,认证之后,cas服务端会将其消毁,生命周期非常的短。
    当我们在集群环境中,用这种方式进行ticket存储的时候,必须注意两种ticket的异同。
1.tgt须长保留时间;
2.st生命周期很短;
3.在集群环境下,st的复制要及时;
4.当数据超过一定量,tgt可能需要进行硬盘持久化。

ehcache进行ticket缓存,配置内容修改,(这里不考虑集群的情况,只是针对单机的cas应用服务器)步骤如下:
1.找到cas/webapp/WEB-INF/spring-configuration/ticketRegistry.xml文件
(对于cas spring配置文件,可参看cas入门之二spring配置文件);
2.删除里面所有bean;

3.增加如下bean:

   <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache-replicated.xml" />
        <property name="shared" value="false" />
        <property name="cacheManagerName" value="ticketRegistryCacheManager" />
    </bean>

    <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.EhCacheTicketRegistry"
    p:serviceTicketsCache-ref="serviceTicketsCache"
    p:ticketGrantingTicketsCache-ref="ticketGrantingTicketsCache" />

<bean id="abstractTicketCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" abstract="true">
<property name="cacheManager" ref="cacheManager" />
        <property name="diskExpiryThreadIntervalSeconds" value="0" />
        <property name="diskPersistent" value="false" />
        <property name="eternal" value="false" />
        <property name="maxElementsInMemory" value="10000" />
        <property name="maxElementsOnDisk" value="0" />
        <property name="memoryStoreEvictionPolicy" value="LRU" />
        <property name="overflowToDisk" value="false" />
        <property name="bootstrapCacheLoader">
            <ref local="ticketCacheBootstrapCacheLoader"/>
        </property>
</bean>

    <bean id="serviceTicketsCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" parent="abstractTicketCache">
        <description>
            Service Tickets (ST) and Proxy Tickets are only valid for short amount of time (default is 10 seconds), and
            most often are removed from the cache when the ST is validated.  The ST cache must be replicated quickly
            since validation is expected within a few second after its creation.  The CAS instance validating the ST may
            not be one that created the ST, since validation is a back-channel service-to-CAS call that is not aware of
            user session affinity.  Synchronous mode is used to ensure all CAS nodes can validate the ST.
        </description>
        <property name="cacheName" value="org.jasig.cas.ticket.ServiceTicket" />
        <property name="cacheEventListeners">
        <ref local="ticketRMISynchronousCacheReplicator"/>
        </property>
        <!-- 
        The maximum number of seconds an element can exist in the cache without being accessed. 
        The element expires at this limit and will no longer be returned from the cache. 
        The default value is 0, which means no TTI eviction takes place (infinite lifetime).
         -->
        <property name="timeToIdle" value="0" />
        <!-- 
        The maximum number of seconds an element can exist in the cache regardless of use. 
        The element expires at this limit and will no longer be returned from the cache. 
        The default value is 0, which means no TTL eviction takes place (infinite lifetime).
        -->
        <property name="timeToLive" value="300" />
    </bean>
    
    <bean id="ticketGrantingTicketsCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean" >
        <description>
            Ticket Granting Tickets (TGT) are valid for the lifetime of the SSO Session.  They become invalid either
            by expiration policy (default 2 hours idle, 8 hours max) or by explicit user sign off via /cas/login.
            The TGT cache can be replicated slowly because TGT are only manipulated via web user started operations
            (mostly grant service ticket) and thus benefit of web session affinity.
        </description>
        <property name="cacheName" value="org.jasig.cas.ticket.TicketGrantingTicket" />
        <property name="cacheEventListeners">
        <ref local="ticketRMIAsynchronousCacheReplicator"/>
        </property>
        <!-- 
        The maximum number of seconds an element can exist in the cache regardless of use. 
        The element expires at this limit and will no longer be returned from the cache. 
        The default value is 0, which means no TTL eviction takes place (infinite lifetime).
        For this sample configuration, 2 hours of inactivity before ticket granting tickets 
        are expired automatically
        -->
        <property name="timeToIdle" value="7201" />
        <!-- 
        The maximum number of seconds an element can exist in the cache without being accessed. 
        The element expires at this limit and will no longer be returned from the cache. 
        The default value is 0, which means no TTI eviction takes place (infinite lifetime).
         -->
        <property name="timeToLive" value="0" />
    </bean>

<bean id="ticketRMISynchronousCacheReplicator" class="net.sf.ehcache.distribution.RMISynchronousCacheReplicator">
   <constructor-arg name="replicatePuts" value="true"/> 
   <constructor-arg name="replicatePutsViaCopy" value="true"/> 
   <constructor-arg name="replicateUpdates" value="true"/>  
   <constructor-arg name="replicateUpdatesViaCopy" value="true"/>  
   <constructor-arg name="replicateRemovals" value="true"/>       
</bean>

<bean id="ticketRMIAsynchronousCacheReplicator" class="net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator" 
                                                parent="ticketRMISynchronousCacheReplicator">
   <constructor-arg name="replicationInterval" value="10000"/>  
   <constructor-arg name="maximumBatchSize" value="100"/>       

</bean>

<bean id="ticketCacheBootstrapCacheLoader" class="net.sf.ehcache.distribution.RMIBootstrapCacheLoader">

        <constructor-arg name="asynchronous" value="true"/>  
        <constructor-arg name="maximumChunkSize" value="5000000"/>  
    </bean>

4.ehcache-replicated.xml文件内容:
<ehcache name="ehCacheTicketRegistryCache" 
updateCheck="false" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
   <diskStore path="java.io.tmpdir/cas"/>
<cacheManagerPeerProviderFactory 
           class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
           properties="peerDiscovery=manual,
rmiUrls=//localhost:40001/org.jasig.cas.ticket.ServiceTicket|//localhost:40001/org.jasig.cas.ticket.TicketGrantingTicket" />
        
   <cacheManagerPeerListenerFactory 
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="port=40001" />
</ehcache>


5.加入cas-server-integration-ehcache-3.5.2.jar
ehcache-core.jar
6.至此配置完毕。

上面的一些bean我是直接从cas 的项目中直接拿过来了,并且已经经过本地测试。在这个配置中我们会发现tgt与st的缓存策略是不同的,并且没有了ticket cleaner,因为用ehcache,我们已经不再需要它了。


cas 入门之十七:ticket 存储方案-memcached ticket 存储

memcached ticket存储器,需要利用memcached客户端jar连接memcached服务器,根据 memcached的服务器特点,服务端是不进行节点内容复制,这个是它的优点,也是它的缺点。比如,现在三台memcached服务器m1,m2,m3,cas 服务端只有一台,当用户认证时,产生tgt123,根据hash算法,tgt123应存在memcached节点m2上, 那么以tgt123产生的st123存在节点m1上。假设此时m2节点不能用了,那么 cas服务器将不会再向m2存储ticket,那么以tgt123票据,发出的请求,将需要重新认证,此时产生tgt456,根据hash算法,将有可能存在节点m3上。那么当节点m2再次上线,存储在它里面的tgt123依然有效,但是再也不会被任何请求使用,所以它只能通过ticket的过期策略去处理。另外,cas服务端向memecached服务器传送ticket的过程是没有加密的,这个过程是否安全,只有我们自已去衡量了。当然我们也可以通过ssh加密通道进行ticket的传送(参见 http://m.udpwork.com/item/1125.html)。
memcached ticket存储的修改步骤:
1.找到cas/webapp/WEB-INF/spring-configuration/ticketRegistry.xml原始文件(cas发布默认配置)
(对于cas spring配置文件,可参看  

cas入门之二spring配置文件

);

2.将  <bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.DefaultTicketRegistry" />

改为

<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.MemCacheTicketRegistry">
<constructor-arg index="0" ref="memcachedClient" />
<!-- TGT timeout in seconds -->
<constructor-arg index="1" value="28800" />
<!-- ST timeout in seconds -->
<constructor-arg index="2" value="10" /></bean>

3.增加

<!--
更多的参数见 http://code.google.com/p/spymemcached/wiki/SpringIntegration 
-->
<bean id="memcachedClient" class="net.spy.memcached.spring.MemcachedClientFactoryBean"
p:servers="host1:11211,host2:11211,host3:11211"
p:protocol="BINARY"
p:locatorType="CONSISTENT"
p:failureMode="Redistribute"
p:transcoder-ref="kryoTranscoder">
<property name="hashAlg">
<util:constant static-field="net.spy.memcached.DefaultHashAlgorithm.${memcached.hashAlgorithm}" />
</property>
</bean>

<bean id="kryoTranscoder"
     class="org.jasig.cas.ticket.registry.support.kryo.KryoTranscoder" init-method="initialize">
<!-- initialBufferSize -->
<constructor-arg index="0" value="8192" /></bean>

4.删除

<!--Quartz -->
<!-- TICKET REGISTRY CLEANER -->
<bean id="ticketRegistryCleaner" class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
p:ticketRegistry-ref="ticketRegistry" />

<bean id="jobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
p:targetObject-ref="ticketRegistryCleaner"
p:targetMethod="clean" />

<bean id="triggerJobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
p:jobDetail-ref="jobDetailTicketRegistryCleaner"
p:startDelay="20000"
p:repeatInterval="5000000" />

虽然memcached节点的ticket会过期,但是过期的ticket的处理由memcached服务器节点自己完成的,所以不需要配置ticket cleaner。

5.增加jar

cas-server-integration-memcached-3.5.2.jar

spymemcached-xxx.jar

kryo-xxx.jar

mockito-core-xxx.jar

0 0
原创粉丝点击