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由来简介

在网上没有找到redisTemplate操作redis集群的例子,所以只能自己动手,在这里简单说一下过程.首先既然redisTemplate依赖jedis,那我们可以认为他内部操作的就是jedis,同理,我们也可以认为他内部也能操作jedisCluster.接下来就在spring-data-redis的源码里面搜一下jedisCluster这个字符串,发现JedisClusterConnection和JedisConnectionFactory中出现了jedisCluster,有没有觉得JedisConnectionFactory很眼熟呢,对,就是配置文件中redisTemplate初始化时候需要用到的连接工厂.现在就可以直接看JedisConnectionFactory
首先,我们来看JedisConnectionFactory,发现里面有一个属性就是jedisCluster,那就看看jedisCluster是如何被初始化的,看下图:



我们可以先看这个方法,这个方法是从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配置文件中的改动(红色标注)

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<?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="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="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="uid" />
<entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
<entry key="groupMembership" value="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="${redis.maxIdle}" />
<!-- 最大建立连接等待时间 -->
<property name="maxWaitMillis" value="${redis.maxWait}" />
<!-- 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个 -->
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean >
 
<!-- 配置文件加载 -->
<bean id="resourcePropertySource" class="org.springframework.core.io.support.ResourcePropertySource">
<constructor-arg name="name" value="redis.cluster.properties"/>
<constructor-arg name="resource" value="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="${redis.password}" />
<property name="timeout" value="${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="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配置文件

 

 

 

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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

 

 

 

 

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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 + "*";
}
}

  

posted @   那家那人那小伙  阅读(4014)  评论(1编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示