我有两个不同的服务(服务 A 和服务 B)共享 Redis 会话,并且这两个服务都使用 Spring-boot 1.5
服务A是身份验证服务(SSO) 而服务 B 是用户服务
近期服务B升级至Spring-boot 2.7。
在这两个服务之间共享session Id已经成为一个问题。
我们不想升级服务 A(至少现在),因为其他服务依赖于它。
如何在不升级服务A的情况下处理Session序列化
我尝试过 Redis 的自定义序列化,但结果失败。
@Configuration
public class RedisConfig {
@Autowired
@Qualifier("springSessionDefaultRedisSerializer")
private RedisSerializer<Object> serializer;
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration("redis", 6379);
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
@Configuration
@Slf4j
public class SpringSessionConfig implements BeanClassLoaderAware {
private ClassLoader loader;
@Bean("springSessionDefaultRedisSerializer")
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer(objectMapper());
}
/**
* Customized {@link ObjectMapper} to add mix-in for class that doesn't have default constructors
*
* @return the {@link ObjectMapper} to use
*/
private ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
return mapper;
}
/*
* @see
* org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang
* .ClassLoader)
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.loader = classLoader;
}
}
这只是一个 hack,不是一个正确的解决方案。 正确的解决方案是将服务升级到相同版本。 使用java反射,我能够将不同的serialVersionUID设置为相同的值。错误已消失,但身份验证未成功。
@PropertySource("classpath:application.yml")
@EnableRedisHttpSession
@SpringBootApplication
public class SingleSignOnApplication extends SpringBootServletInitializer {
@Value("${spring.session.timeout}")
private Integer maxInactiveInterval;
@Bean
public RedisOperationsSessionRepository sessionRepository(RedisConnectionFactory factory) {
RedisOperationsSessionRepository sessionRepository =
new RedisOperationsSessionRepository(factory);
sessionRepository.setDefaultMaxInactiveInterval(maxInactiveInterval);
return sessionRepository;
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SingleSignOnApplication.class);
}
public static void main(String[] args) throws Exception {
updateSerialVersionUID(SecurityContextImpl.class);
updateSerialVersionUID(UsernamePasswordAuthenticationToken.class);
updateSerialVersionUID(SimpleGrantedAuthority.class);
updateSerialVersionUID(WebAuthenticationDetails.class);
updateSerialVersionUID(LdapUserDetailsImpl.class);
updateSerialVersionUID(AnonymousAuthenticationToken.class);
SpringApplication.run(SingleSignOnApplication.class, args);
}
private static void updateSerialVersionUID(Class<?> clazzToCustomize) throws Exception {
Field staticFinalFieldToReplace = clazzToCustomize.getDeclaredField("serialVersionUID");
setFinalStatic(staticFinalFieldToReplace, 570L);
}
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
AccessController.doPrivileged((PrivilegedAction) () -> {
modifiersField.setAccessible(true);
return null;
} );
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
}