提交 0a5fdf63 编写于 作者: L lpphan 提交者: 晴天丶en

过期key监听器

上级 4224ca83
......@@ -39,6 +39,7 @@ import java.util.List;
*/
public class CustomRedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String ACCESS_BAK = "access_bak:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
......@@ -118,7 +119,7 @@ public class CustomRedisTokenStore implements TokenStore {
}
private ClientDetails deserializeClientDetails(byte[] bytes) {
return (ClientDetails)redisValueSerializer.deserialize(bytes);
return (ClientDetails) redisValueSerializer.deserialize(bytes);
}
private byte[] serialize(String string) {
......@@ -166,7 +167,7 @@ public class CustomRedisTokenStore implements TokenStore {
//获取过期时长
int validitySeconds = getAccessTokenValiditySeconds(clientAuth.getClientId());
if (validitySeconds > 0) {
double expiresRatio = token.getExpiresIn() / (double)validitySeconds;
double expiresRatio = token.getExpiresIn() / (double) validitySeconds;
//判断是否需要续签,当前剩余时间小于过期时长的50%则续签
if (expiresRatio <= securityProperties.getAuth().getRenew().getTimeRatio()) {
//更新AccessToken过期时间
......@@ -182,6 +183,7 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 判断应用自动续签是否满足白名单和黑名单的过滤逻辑
*
* @param clientId 应用id
* @return 是否满足
*/
......@@ -193,7 +195,7 @@ public class CustomRedisTokenStore implements TokenStore {
List<String> exclusiveClientIds = securityProperties.getAuth().getRenew().getExclusiveClientIds();
if (includeClientIds.size() > 0) {
result = includeClientIds.contains(clientId);
} else if(exclusiveClientIds.size() > 0) {
} else if (exclusiveClientIds.size() > 0) {
result = !exclusiveClientIds.contains(clientId);
}
return result;
......@@ -201,6 +203,7 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 获取token的总有效时长
*
* @param clientId 应用id
*/
private int getAccessTokenValiditySeconds(String clientId) {
......@@ -256,12 +259,14 @@ public class CustomRedisTokenStore implements TokenStore {
/**
* 存储token
*
* @param isRenew 是否续签
*/
private void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication, boolean isRenew) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] accessBakKey = serializeKey(ACCESS_BAK + token.getValue());
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(SecurityConstants.REDIS_UNAME_TO_ACCESS + getApprovalKey(authentication));
......@@ -279,6 +284,7 @@ public class CustomRedisTokenStore implements TokenStore {
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, accessBakKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth);
this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken);
} catch (Exception ex) {
......@@ -286,6 +292,7 @@ public class CustomRedisTokenStore implements TokenStore {
}
} else {
conn.set(accessKey, serializedAccessToken);
conn.set(accessBakKey, serializedAccessToken);
conn.set(authKey, serializedAuth);
conn.set(authToAccessKey, serializedAccessToken);
}
......@@ -303,6 +310,7 @@ public class CustomRedisTokenStore implements TokenStore {
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, seconds);
conn.expire(accessBakKey, seconds + 60);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
......@@ -363,6 +371,7 @@ public class CustomRedisTokenStore implements TokenStore {
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] accessBakKey = serializeKey(ACCESS_BAK + tokenValue);
byte[] authKey = serializeKey(SecurityConstants.REDIS_TOKEN_AUTH + tokenValue);
byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
RedisConnection conn = getConnection();
......@@ -371,6 +380,7 @@ public class CustomRedisTokenStore implements TokenStore {
byte[] auth = conn.get(authKey);
conn.openPipeline();
conn.del(accessKey);
conn.del(accessBakKey);
conn.del(accessToRefreshKey);
// Don't remove the refresh token - it's up to the caller to do that
conn.del(authKey);
......
......@@ -154,6 +154,10 @@ public interface SecurityConstants {
* redis中授权token对应的key
*/
String REDIS_TOKEN_AUTH = "auth:";
/**
* 值同access 过期时间+60
*/
String ACCESS_BAK = "access_bak:";
/**
* redis中应用对应的token集合的key
*/
......
package com.central.common.redis.template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisConnection;
......@@ -178,6 +179,26 @@ public class RedisRepository {
public Object get(final String key) {
return redisTemplate.opsForValue().get(key);
}
/**
*获取原来key键对应的值并重新赋新值。
* @param key
* @param value
* @return
*/
public String getAndSet(final String key,Object value) {
String result = null;
if (StringUtils.isEmpty(key)){
log.error("key不能为空");
return null;
}
try {
result = (String) redisTemplate.opsForValue().getAndSet(key, value);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
/**
* 根据key获取对象
*
......@@ -190,6 +211,7 @@ public class RedisRepository {
return redisTemplate.execute(connection -> deserializeValue(connection.get(rawKey), valueSerializer), true);
}
/**
* Ops for hash hash operations.
*
......
package com.central.oauth.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
*
* redis过期key监听器配置类
* @author zlt
*
*/
@Configuration
public class RedisListenerConfig {
@Bean
@Primary
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
package com.central.oauth.listener;
import com.central.common.constant.SecurityConstants;
import com.central.common.redis.template.RedisRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.stereotype.Component;
/**
*
* 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 = redisRepository.getAndSet("qc:" + accessValue, "1");
if (StringUtils.isNotEmpty(qc) && "1".equals(qc)) {
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();
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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册