首页 > 代码库 > CAS工程用redis集群存储票据ticket Spring整合
CAS工程用redis集群存储票据ticket Spring整合
maven jar包版本:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.7.4.RELEASE</version> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.3.RELEASE</version> </dependency>
1:RedisTemplate由来简介
我们可以先看这个方法,这个方法是从InitializingBean中实现的方法,是spring初始化bean的时候需要调用的.所以可以假装认为只要实例化了JedisConnectionFactory就可以实例化jedisCluster,但是不要忘了有一个条件,那就是clusterConfig不能为空,接下来我们找clusterConfig是如何被实例化的.发现JedisConnectionFactory有一个构造函数
JedisConnectionFactory(RedisClusterConfiguration clusterConfig, JedisPoolConfig poolConfig).这下就好办了JedisPoolConfig我们本来就认识,RedisClusterConfiguration是需要我们实例化的,接下来就看看RedisClusterConfiguration,一进来RedisClusterConfiguration我们就能看到个好东西,见下图:
我们可以看RedisClusterConfiguration的注释,虽然没有说明,但是光看格式,就大概能猜到这些东西应该是写到properties文件里面的,而构造函数的参数又正好是propertySource,很容易就能联想到ResourcePropertySource,接下来就简单了,直接开始开干了.
2:cas工程中deployerConfigContext.xml配置文件中的改动(红色标注)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 引入配置文件 --> <!-- <bean id="propertyConfigurer" --> <!-- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> --> <!-- <property name="location" value=http://www.mamicode.com/"classpath:redis.properties" /> --> <!-- </bean> --> <util:map id="authenticationHandlersResolvers"> <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /> <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> </util:map> <util:list id="authenticationMetadataPopulators"> <ref bean="successfulHandlerMetaDataPopulator" /> <ref bean="rememberMeAuthenticationMetaDataPopulator" /> </util:list> <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao" p:backingMap-ref="attrRepoBackingMap" /> <!-- <alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" /> --> <!--begin 从数据库中的用户表中读取 --> <bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName"> <constructor-arg value=http://www.mamicode.com/"MD5"/> </bean> <bean id="queryDatabaseAuthenticationHandler" name="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="passwordEncoder" ref="MD5PasswordEncoder"/> </bean> <alias name="dataSource" alias="queryDatabaseDataSource"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://192.168.103.169:3306/userdb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" p:user="root" p:password="123" p:initialPoolSize="6" p:minPoolSize="6" p:maxPoolSize="18" p:maxIdleTimeExcessConnections="120" p:checkoutTimeout="10000" p:acquireIncrement="6" p:acquireRetryAttempts="5" p:acquireRetryDelay="2000" p:idleConnectionTestPeriod="30" p:preferredTestQuery="select 1"/> <!--end 从数据库中的用户表中读取 --> <alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" /> <util:map id="attrRepoBackingMap"> <entry key="uid" value=http://www.mamicode.com/"uid" /> <entry key="eduPersonAffiliation" value=http://www.mamicode.com/"eduPersonAffiliation" /> <entry key="groupMembership" value=http://www.mamicode.com/"groupMembership" /> <entry> <key><value>memberOf</value></key> <list> <value>faculty</value> <value>staff</value> <value>org</value> </list> </entry> </util:map> <alias name="serviceThemeResolver" alias="themeResolver" /> <alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" /> <!-- <alias name="defaultTicketRegistry" alias="ticketRegistry" /> --> <!-- 票据保存方式及有效期设置 --> <alias name="redisTicketRegistry" alias="ticketRegistry" /> <!-- <bean id="redisTicketRegistry" class="com.hivescm.cas.ticket.registry.RedisTicketRegistry" --> <!-- p:client-ref="ticketRedisTemplate" --> <!-- p:tgtTimeout="28800" --> <!-- p:stTimeout="10"/> --> <bean id="redisTicketRegistry" class="com.hivescm.cas.ticket.registry.RedisClusterTicketRegistry" p:client-ref="redisTemplate" p:tgtTimeout="28800" p:stTimeout="10"/> <!-- redis连接池 --> <!-- <bean id="jedisConnFactory" --> <!-- class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" --> <!-- p:hostName="192.168.103.158" --> <!-- p:database="10" --> <!-- p:usePool="true"/> --> <!-- <bean id="ticketRedisTemplate" class="com.hivescm.cas.ticket.registry.TicketRedisTemplate" --> <!-- p:connectionFactory-ref="jedisConnFactory"/> --> <!-- jedis 配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <!-- 最大空闲数 --> <property name="maxIdle" value=http://www.mamicode.com/"${redis.maxIdle}" /> <!-- 最大建立连接等待时间 --> <property name="maxWaitMillis" value=http://www.mamicode.com/"${redis.maxWait}" /> <!-- 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 --> <property name="testOnBorrow" value=http://www.mamicode.com/"${redis.testOnBorrow}" /> </bean > <!-- 配置文件加载 --> <bean id="resourcePropertySource" class="org.springframework.core.io.support.ResourcePropertySource"> <constructor-arg name="name" value=http://www.mamicode.com/"redis.cluster.properties"/> <constructor-arg name="resource" value=http://www.mamicode.com/"classpath:redis.cluster.properties"/> </bean> <!-- redisCluster配置 --> <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration"> <constructor-arg name="propertySource" ref="resourcePropertySource"/> </bean> <!-- redis服务器中心 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/> <constructor-arg name="poolConfig" ref="poolConfig"/> <property name="password" value=http://www.mamicode.com/"${redis.password}" /> <property name="timeout" value=http://www.mamicode.com/"${redis.timeout}" ></property> </bean > <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="connectionFactory" /> <!-- 如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can‘t cast to String!! --> <property name="keySerializer" > <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer" > <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> </property> </bean > <!-- <bean id="redisHttpSessionConfiguration" --> <!-- class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> --> <!-- 超时时间,默认1800秒 --> <!-- <property name="maxInactiveIntervalInSeconds" value=http://www.mamicode.com/"1800" /> --> <!-- </bean> --> <alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" /> <alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" /> <alias name="anyAuthenticationPolicy" alias="authenticationPolicy" /> <alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" /> <bean id="auditTrailManager" class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager" p:entrySeparator="${cas.audit.singleline.separator:|}" p:useSingleLine="${cas.audit.singleline:false}"/> <alias name="neverThrottle" alias="authenticationThrottle" /> <util:list id="monitorsList"> <ref bean="memoryMonitor" /> <ref bean="sessionMonitor" /> </util:list> <alias name="defaultPrincipalFactory" alias="principalFactory" /> <alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" /> <alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" /> <alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" /> </beans>
3:redis.cluster.properties配置文件
#redis\u4E2D\u5FC3 #redis\u7684\u670D\u52A1\u5668\u5730\u5740 redis.host=192.168.103.158 #redis\u7684\u670D\u52A1\u7AEF\u53E3 redis.port=6379 #\u5BC6\u7801 redis.password= #\u6700\u5927\u7A7A\u95F2\u6570 redis.maxIdle=100 #\u6700\u5927\u8FDE\u63A5\u6570 redis.maxActive=300 #\u6700\u5927\u5EFA\u7ACB\u8FDE\u63A5\u7B49\u5F85\u65F6\u95F4 redis.maxWait=1000 #\u5BA2\u6237\u7AEF\u8D85\u65F6\u65F6\u95F4\u5355\u4F4D\u662F\u6BEB\u79D2 redis.timeout=100000 redis.maxTotal=1000 redis.minIdle=8 #\u660E\u662F\u5426\u5728\u4ECE\u6C60\u4E2D\u53D6\u51FA\u8FDE\u63A5\u524D\u8FDB\u884C\u68C0\u9A8C,\u5982\u679C\u68C0\u9A8C\u5931\u8D25,\u5219\u4ECE\u6C60\u4E2D\u53BB\u9664\u8FDE\u63A5\u5E76\u5C1D\u8BD5\u53D6\u51FA\u53E6\u4E00\u4E2A redis.testOnBorrow=true #sentinel #spring.redis.sentinel.node1.host=127.0.0.1 #spring.redis.sentinel.node2.host=127.0.0.1 #spring.redis.sentinel.node3.host=127.0.0.1 #spring.redis.sentinel.node1.port=26379 #spring.redis.sentinel.node2.port=26479 #spring.redis.sentinel.node3.port=26579 #sentinel #jediscluster #cluster1.host.port=127.0.0.1:7000 #cluster2.host.port=127.0.0.1:7001 #cluster3.host.port=127.0.0.1:7002 #cluster4.host.port=127.0.0.1:7003 #cluster5.host.port=127.0.0.1:7004 #cluster6.host.port=127.0.0.1:7005 #cluster7.host.port=127.0.0.1:7006 #cluster8.host.port=127.0.0.1:7007 #jediscluster #rediscluster #spring.redis.cluster.nodes=192.168.103.158:6379 spring.redis.cluster.nodes=192.168.103.174:6379,192.168.103.174:6389,192.168.103.174:6399,192.168.103.173:6379,192.168.103.173:6389,192.168.103.173:6399 spring.redis.cluster.max-redirects=3
4. CAS用redis集群存储ticket的自定义实现类:RedisClusterTicketRegistry.java
package com.hivescm.cas.ticket.registry; import org.jasig.cas.ticket.ServiceTicket; import org.jasig.cas.ticket.Ticket; import org.jasig.cas.ticket.TicketGrantingTicket; import org.jasig.cas.ticket.registry.encrypt.AbstractCrypticTicketRegistry; import org.springframework.beans.factory.DisposableBean; import org.springframework.data.redis.core.RedisTemplate; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; /** * Key-value ticket registry implementation that stores tickets in redis keyed on the ticket ID. * * @author serv */ public final class RedisClusterTicketRegistry extends AbstractCrypticTicketRegistry implements DisposableBean { private final static String TICKET_PREFIX = "CAS:TICKET:"; /** * redis client. */ @NotNull private RedisTemplate client; /** * TGT cache entry timeout in seconds. */ @Min(0) private int tgtTimeout; /** * ST cache entry timeout in seconds. */ @Min(0) private int stTimeout; public void setClient(RedisTemplate client) { this.client = client; } public void setTgtTimeout(int tgtTimeout) { this.tgtTimeout = tgtTimeout; } public void setStTimeout(int stTimeout) { this.stTimeout = stTimeout; } public RedisClusterTicketRegistry() { } /** * Creates a new instance using the given redis client instance, which is presumably configured via * <code>net.spy.redis.spring.redisClientFactoryBean</code>. * * @param client redis client. * @param ticketGrantingTicketTimeOut TGT timeout in seconds. * @param serviceTicketTimeOut ST timeout in seconds. */ public RedisClusterTicketRegistry(final RedisTemplate client, final int ticketGrantingTicketTimeOut, final int serviceTicketTimeOut) { this.tgtTimeout = ticketGrantingTicketTimeOut; this.stTimeout = serviceTicketTimeOut; this.client = client; } protected void updateTicket(final Ticket ticket) { logger.debug("Updating ticket {}", ticket); try { String redisKey = this.getTicketRedisKey(ticket.getId()); this.client.boundValueOps(redisKey).set(ticket, getTimeout(ticket), TimeUnit.SECONDS); } catch (final Exception e) { logger.error("Failed updating {}", ticket, e); } } public void addTicket(final Ticket ticket) { logger.debug("Adding ticket {}", ticket); try { String redisKey = this.getTicketRedisKey(ticket.getId()); this.client.boundValueOps(redisKey).set(ticket, getTimeout(ticket), TimeUnit.SECONDS); } catch (final Exception e) { logger.error("Failed Adding {}", ticket, e); } } public boolean deleteTicket(final String ticketId) { logger.debug("Deleting ticket {}", ticketId); try { this.client.delete(this.getTicketRedisKey(ticketId)); return true; } catch (final Exception e) { logger.error("Failed deleting {}", ticketId, e); } return false; } public Ticket getTicket(final String ticketId) { try { final Ticket t = (Ticket) this.client.boundValueOps(this.getTicketRedisKey(ticketId)).get(); if (t != null) { return getProxiedTicketInstance(t); } } catch (final Exception e) { logger.error("Failed fetching {} ", ticketId, e); } return null; } /** * {@inheritDoc} * This operation is not supported. * * @throws UnsupportedOperationException if you try and call this operation. */ public Collection<Ticket> getTickets() { Set<Ticket> tickets = new HashSet<Ticket>(); Set<String> keys = this.client.keys(this.getPatternTicketRedisKey()); for (String key : keys) { Ticket ticket = (Ticket) this.client.boundValueOps(key).get(); if (ticket == null) { this.client.delete(key); } else { tickets.add(ticket); } } return tickets; } public void destroy() throws Exception { client.getConnectionFactory().getConnection().close(); } @Override protected boolean needsCallback() { return true; } private int getTimeout(final Ticket t) { if (t instanceof TicketGrantingTicket) { return this.tgtTimeout; } else if (t instanceof ServiceTicket) { return this.stTimeout; } throw new IllegalArgumentException("Invalid ticket type"); } //Add a prefix as the key of redis private String getTicketRedisKey(String ticketId) { return TICKET_PREFIX + ticketId; } // pattern all ticket redisKey private String getPatternTicketRedisKey() { return TICKET_PREFIX + "*"; } }
CAS工程用redis集群存储票据ticket Spring整合