如何处理具有两个不同serialVersionUID的Redis SecurityContext序列化

问题描述 投票:0回答:1

我有两个不同的服务(服务 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;
    }
}
spring-boot spring-security spring-data-redis spring-session
1个回答
-1
投票

这只是一个 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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.