概述
Spring security的TokenStore redis实现中,命名空间client_id_to_access与uname_to_access下list数据过期时间是伴随着整个list的,redis的策略就是新增list数据就会刷新整个key的过期时间,这就会导致与同一个clientId相关的token过期后改命名空间下的key却没有被销毁,导致内存溢出。
解决方法
利用redis监听过期key事件回调方法处理client_id_to_access中的过期数据
1、启动Redis的过期key监听功能
在redis.conf配置文件中把#notify-keyspace-events Ex
这一行注释打开;
2、改造TokenStore 的redis缓存实现
主要是在RedisTokenStore的storeAccessToken方法中增加一个与access命名空间相同的数据,过期时间要适当的比access命名空间中的key要长一点,可以自定义命名,比如access_bak。这样做的目的是在access的过期key触发监听器的时候可以从相应的access_bak中获取到相同的内容,因为Redis过期key触发回调后只能在监听器获取到key的值,但是内容就已经被删除了,但是我们需要相应key的内容来删除client_id_to_access中的数据。
相关代码省略,对storeAccessToken方法进行重写即可。
3、增加redis过期key监听器,获取过期数据并删除过期数据
利用redis销毁过期key的回调机制,新增一个监听器处理过期数据。
/**
*
* redis过期key监听器
* @author zlt
*
*/
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
@Autowired
private RedisRepository redisRepository;
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private final RedisConnectionFactory connectionFactory;
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer, RedisConnectionFactory connectionFactory) {
super(listenerContainer);
this.connectionFactory = connectionFactory;
}
@Override
public void onMessage(Message message, byte[] pattern) {
if (message == null) {
log.debug("message不能为空");
return;
}
//获取失效的的key
String expiredKey = message.toString();
if (StringUtils.isEmpty(expiredKey)) {
log.debug("expiredKey不能为空");
return;
}
String accesskey = expiredKey.substring(0, expiredKey.indexOf(":") + 1);
if (!"access:".equals(accesskey)) {
log.debug("非需要监听key,跳过");
return;
}
String accessValue = expiredKey.substring(expiredKey.indexOf(":") + 1);
// 分布式集群部署下防止一个过期被多个服务重复消费
String qc = "qc:" + accessValue;
String oldLock = redisRepository.getAndSet(qc, "1");
if (StringUtils.isNotEmpty(oldLock) && "1".equals(oldLock)) {
log.debug("其他节点已经处理了该数据,跳过");
return;
}
byte[] accessBakKey = serializeKey(SecurityConstants.ACCESS_BAK + accessValue);
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + accessValue);
RedisConnection conn = getConnection();
try {
byte[] access = conn.get(accessBakKey);
byte[] auth = conn.get(authKey);
OAuth2Authentication authentication = deserializeAuthentication(auth);
if (authentication != null) {
byte[] unameKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(SecurityConstants.REDIS_CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
conn.openPipeline();
conn.lRem(unameKey, 1, access);
conn.lRem(clientId, 1, access);
conn.closePipeline();
}
} catch (Exception e) {
log.error(e.getMessage());
} finally {
conn.del(oldLock.getBytes());
conn.close();
}
}
private byte[] serializeKey(String object) {
return serialize("" + object);
}
private byte[] serialize(String string) {
return serializationStrategy.serialize(string);
}
private RedisConnection getConnection() {
return connectionFactory.getConnection();
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? ""
: authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
}
配置监听器:
/**
*
* redis过期key监听器配置类
* @author zlt
*
*/
@Configuration
public class RedisListenerConfig {
@Bean
@Primary
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
return container;
}
}
最后
以上就是勤劳大山为你收集整理的spring security oauth2中redis下client_id_to_access命名空间过期数据过多导致内存溢出问题的全部内容,希望文章能够帮你解决spring security oauth2中redis下client_id_to_access命名空间过期数据过多导致内存溢出问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复