RedisTokenStore:Json序列化
一. 前言
Spring Security Oauth2 存储Token的方式有多种, 比如JWT、Jdbc(数据库)、Redis等,但是对于一个大型的分布式服务应用,Redis存储方式应该是最佳选择。
二. 问题
我们使用默认的Redis存储方式,序列化到到Redis的数据是采用JDK序列化策略写入到redis的。这样对于程序的功能毫无影响,但是对于开发者却很不直观,出现问题,也不容易排查,我们能不能把它们序列化成JSON格式呢?
默认序列化方式
Spring Security Oauth2 Redis序列化Token相关的数据是采用JdkSerializationStrategy,用这个序列化策略序列化出的结果正如上图所示那样,具体的代码如下:
//org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore
public class RedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private static final boolean springDataRedis_2_0 = ClassUtils.isPresent(
"org.springframework.data.redis.connection.RedisStandaloneConfiguration",
RedisTokenStore.class.getClassLoader());
private final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
//Jdk序列方式
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
......省略无关代码
Fastjson序列化策略
笔者首先想到的是自定义一个RedisTokenStoreSerializationStrategy**,**接口的实现改成Object和JSONString之间的序列化、反序列应该就好了,于是笔者使用Fastjson实现了一个序列化策略,并注入到Spring Bean容器中,如下:
public class FastjsonRedisTokenStoreSerializationStrategy implements RedisTokenStoreSerializationStrategy {
private final static ParserConfig defaultRedisConfig = new ParserConfig();
static {
defaultRedisConfig.setAutoTypeSupport(true);
}
static {
//设置Fastjson Json自动转换为Java对象
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
@Override
public <T> T deserialize(byte[] bytes, Class<T> clazz) {
Preconditions.checkArgument(clazz != null,
"clazz can't be null");
if (bytes == null || bytes.length == 0) {
return null;
}
try {
return JSON.parseObject(new String(bytes, IOUtils.UTF8), clazz, defaultRedisConfig);
} catch (Exception ex) {
throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
}
}
@Override
public String deserializeString(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
return new String(bytes, IOUtils.UTF8);
}
@Override
public byte[] serialize(Object object) {
if (object == null) {
return new byte[0];
}
try {
return JSON.toJSONBytes(object, SerializerFeature.WriteClassName,
SerializerFeature.DisableCircularReferenceDetect);
} catch (Exception ex) {
throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
}
}
@Override
public byte[] serialize(String data) {
if (data == null || data.length() == 0) {
return new byte[0];
}
return data.getBytes(Charset.forName("utf-8"));
}
}
@Bean
public RedisTokenStore redisTokenStore(){
RedisTokenStore store = new RedisTokenStore(redisConnectionFactory);
store.setSerializationStrategy(new FastjsonRedisTokenStoreSerializationStrategy());
return store;
}
笔者刚开始以为这样处理就可以了,OAuth2Authentication 这个类并没有默认构造方法,会导致Fastjson反序列化失败,因此针对Oauth2等无法序列化或者反序列化的类需要特殊化处理。针对DefaultOAuth2RefreshToken、OAuth2Authentication等特殊的类,我们需要定制化序列化以及反序列化策略。下面是笔者项目的配置以及序列化和反序列策略,有些类是满足需求定制的,不在Spring Security包中。提供只是参考思路,不可照抄。配置和自定义策略如下:
public class FastjsonRedisTokenStoreSerializationStrategy implements RedisTokenStoreSerializationStrategy {
private static ParserConfig config = new ParserConfig(<