Spring Redis自定义OidcUser跨应用会话共享配置咨询
解决方案
方案1:主应用存储会话时转换为标准OidcUser
核心思路是不在Redis中存储自定义的CustomOidcUser实例,而是在认证成功后将其转换为Spring Security提供的标准OidcUser再存入会话:
- 自定义认证成功处理器,替换Authentication中的用户对象:
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if (authentication.getPrincipal() instanceof CustomOidcUser customUser) { // 基于自定义用户的信息构建标准OidcUser OidcUser standardOidcUser = new DefaultOidcUser( customUser.getAuthorities(), customUser.getIdToken(), customUser.getUserInfo() ); // 生成新的认证对象并更新上下文 Authentication newAuth = new OAuth2AuthenticationToken( standardOidcUser, standardOidcUser.getAuthorities(), authentication.getAuthorizedClientRegistrationId() ); SecurityContextHolder.getContext().setAuthentication(newAuth); } super.onAuthenticationSuccess(request, response, authentication); } }
- 在Spring Security配置中启用该处理器:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .successHandler(customAuthenticationSuccessHandler()) ); return http.build(); } @Bean public CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler() { return new CustomAuthenticationSuccessHandler(); } }
方案2:共享CustomOidcUser类并统一Redis序列化
如果需要保留自定义用户的额外属性,可将CustomOidcUser抽为公共依赖Jar包,让所有共享会话的应用引入,同时统一Redis序列化配置确保反序列化兼容:
- 所有应用配置统一的Redis会话序列化器,启用类型信息以识别自定义类:
@Configuration public class RedisSessionConfig { @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); // 启用类型信息,让反序列化时能识别自定义类 mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); serializer.setObjectMapper(mapper); return serializer; } }
方案3:自定义UserDetails序列化/反序列化逻辑
通过自定义序列化规则,在主应用中将CustomOidcUser序列化为标准OidcUser的JSON结构,其他应用直接反序列化为标准OidcUser:
- 主应用自定义序列化器,只序列化标准OidcUser所需字段:
public class OidcUserSerializer extends JsonSerializer<CustomOidcUser> { @Override public void serialize(CustomOidcUser value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); gen.writeObjectField("authorities", value.getAuthorities()); gen.writeObjectField("idToken", value.getIdToken()); gen.writeObjectField("userInfo", value.getUserInfo()); gen.writeEndObject(); } }
- 其他应用自定义反序列化器,将JSON结构转为标准OidcUser:
public class OidcUserDeserializer extends JsonDeserializer<OidcUser> { @Override public OidcUser deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); Collection<? extends GrantedAuthority> authorities = ctxt.readValue(node.get("authorities"), new TypeReference<>() {}); OidcIdToken idToken = ctxt.readValue(node.get("idToken"), OidcIdToken.class); OidcUserInfo userInfo = ctxt.readValue(node.get("userInfo"), OidcUserInfo.class); return new DefaultOidcUser(authorities, idToken, userInfo); } }
- 在各自应用的ObjectMapper中注册对应序列化/反序列化器(通过Mixin方式实现):
// 主应用配置 mapper.addMixIn(CustomOidcUser.class, OidcUserSerializerMixin.class); abstract class OidcUserSerializerMixin { @JsonSerialize(using = OidcUserSerializer.class) abstract CustomOidcUser getPrincipal(); } // 其他应用配置 mapper.addMixIn(OidcUser.class, OidcUserDeserializerMixin.class); abstract class OidcUserDeserializerMixin { @JsonDeserialize(using = OidcUserDeserializer.class) abstract OidcUser getPrincipal(); }
内容的提问来源于stack exchange,提问作者yaroslav96
相关产品推荐
相关产品推荐

