Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newly created session message can not be deserialized by RedisIndexedSessionRepository correctly when using GenericJackson2JsonRedisSerializer #2227

Closed
buzzerrookie opened this issue Jan 19, 2023 · 7 comments
Assignees
Labels
in: redis status: superseded An issue that has been superseded by another

Comments

@buzzerrookie
Copy link

Describe the bug
In spring-session-data-redis-3.0.0, when using GenericJackson2JsonRedisSerializer as the default serializer, the newly created session message can not be deserialized by onMessage method in RedisIndexedSessionRepository correctly, and the error message "java.lang.IllegalArgumentException: The class with java.lang.Long and name of java.lang.Long is not in the allowlist. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See spring-projects/spring-security#4370 for details" always appears for new sessions.

To Reproduce
I'm using spring-boot 3.0.1 with dependencies spring-boot-starter-data-redis, spring-boot-starter-security, spring-boot-starter-web and spring-session-data-redis, and I'm using Redis 6.2.10.

  1. Start the SessionDemoApplication in the sample project below.
  2. Use Postman to post a request to localhost:8090/login, with user and password for username and password parameters respectively.
  3. The response will be success.
  4. Then the error message appears in the console.

Expected behavior
No error message should appear in the console.

Sample
session-demo3.zip

I tried to find the reason, and the followings are what I have found:

  1. The method saveDelta in RedisIndexedSessionRepository has been called to save the data to Redis.
  2. Then, the whole new session data will be published to Redis.
  3. RedisIndexedSessionRepository will receive the above message through its own onMessage method.
  4. In the onMessage method, the new session data will be deserialized by the default serializer, which in my case is the GenericJackson2JsonRedisSerializer.
  5. The field lastAccessedTime and creationTime in session data are of type java.lang.Long, and Long is not in the allowlist in class SecurityJackson2Modules. Thus the error occurs.
  6. When I tried the previous vesion such as spring boot 2.7.7, there were no such errors. Because the method saveDelta in RedisIndexedSessionRepository has been changed by the following commit, Fix SessionCreatedEvent handling in RedisIndexedSessionRepository.

Should I register a Mixin for Long by myself ? Or would you please add Long to the allowlist in class SecurityJackson2Modules ? Thank you very much.

@marcusdacoregio
Copy link
Contributor

Hi @buzzerrookie, thanks for the report.

In the meantime, as a workaround, you can register a custom mixin for the Long class:

private ObjectMapper objectMapper() {
	ObjectMapper mapper = new ObjectMapper();
	mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
	mapper.addMixIn(Long.class, LongMixin.class);
	return mapper;
}

abstract class LongMixin {
	@SuppressWarnings("unused")
	@JsonProperty("long")
	Long value;
}

@marcusdacoregio
Copy link
Contributor

When I tried the previous vesion such as spring boot 2.7.7, there were no such errors. Because the method saveDelta in RedisIndexedSessionRepository has been changed by the following commit, Fix SessionCreatedEvent handling in RedisIndexedSessionRepository.

I don't think I follow, RedisIndexedSessionRepository was introduced in 3.0 and you mention that the error doesn't happen in 2.7.7? Am I missing something?

@buzzerrookie
Copy link
Author

Hi @buzzerrookie, thanks for the report.

In the meantime, as a workaround, you can register a custom mixin for the Long class:

private ObjectMapper objectMapper() {
	ObjectMapper mapper = new ObjectMapper();
	mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
	mapper.addMixIn(Long.class, LongMixin.class);
	return mapper;
}

abstract class LongMixin {
	@SuppressWarnings("unused")
	@JsonProperty("long")
	Long value;
}

Thank you for the workaround. Currently I wrote a Mixin myself.
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) @JsonDeserialize(using = NumberDeserializers.LongDeserializer.class) @JsonIgnoreProperties(ignoreUnknown = true) public class LongMixin { }

@buzzerrookie
Copy link
Author

When I tried the previous vesion such as spring boot 2.7.7, there were no such errors. Because the method saveDelta in RedisIndexedSessionRepository has been changed by the following commit, Fix SessionCreatedEvent handling in RedisIndexedSessionRepository.

I don't think I follow, RedisIndexedSessionRepository was introduced in 3.0 and you mention that the error doesn't happen in 2.7.7? Am I missing something?

According to the javadoc, the RedisIndexedSessionRepository was introduced in 2.2.0. I mean the method saveDelta is fixed in 2.7.7.

@marcusdacoregio
Copy link
Contributor

RedisIndexedSessionRepository was introduced in 2.2.0

That's right, it was an oversight from my side, apologies.

@marcusdacoregio
Copy link
Contributor

Closing in favor of #2305

@marcusdacoregio marcusdacoregio added status: superseded An issue that has been superseded by another and removed type: bug A general bug labels May 30, 2023
@soobinJung
Copy link

Issue: Unable to Store Spring Security Managed Objects in Redis after Upgrading Spring Session from 2.x.x to 3.x.x

Environment:

  • Spring Session version: 3.x.x
  • Previous Spring Session version: 2.x.x
  • Spring Security version: [Your Spring Security version]
  • Serialization library: Jackson

Problem Description:
After upgrading from Spring Session 2.x.x to 3.x.x, I am experiencing issues storing objects managed by Spring Security in Redis. The serialization fails and throws the following error:

java.lang.ClassNotFoundException: com.fasterxml.jackson.annotation.JsonTypeInfo$Value
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.findPolymorphicTypeInfo(JacksonAnnotationIntrospector.java:670) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector._findTypeResolver(JacksonAnnotationIntrospector.java:1579) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector.findPropertyTypeResolver(JacksonAnnotationIntrospector.java:692) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findPropertyTypeSerializer(BeanSerializerFactory.java:313) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:876) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:634) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanOrAddOnSerializer(BeanSerializerFactory.java:402) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanOrAddOnSerializer(BeanSerializerFactory.java:295) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:240) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:174) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1525) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1473) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.SerializerProvider.findPrimaryPropertySerializer(SerializerProvider.java:739) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap.findAndAddPrimarySerializer(PropertySerializerMap.java:64) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter._findAndAddDynamic(BeanPropertyWriter.java:901) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:710) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:653) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:502) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:341) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4793) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(ObjectMapper.java:4061) ~[jackson-databind-2.16.2.jar!/:2.16.2]
	at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.serialize(GenericJackson2JsonRedisSerializer.java:252) ~[spring-data-redis-3.2.3.jar!/:3.2.3]
	at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:186) ~[spring-data-redis-3.2.3.jar!/:3.2.3]
	at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:161) ~[spring-data-redis-3.2.3.jar!/:3.2.3]
	at org.springframework.session.data.redis.RedisSessionRepository$RedisSession.saveDelta(RedisSessionRepository.java:328) ~[spring-session-data-redis-3.2.1.jar!/:3.2.1]
	at org.springframework.session.data.redis.RedisSessionRepository$RedisSession.save(RedisSessionRepository.java:306) ~[spring-session-data-redis-3.2.1.jar!/:3.2.1]
	at org.springframework.session.data.redis.RedisSessionRepository.save(RedisSessionRepository.java:132) ~[spring-session-data-redis-3.2.1.jar!/:3.2.1]
	at org.springframework.session.data.redis.RedisSessionRepository.save(RedisSessionRepository.java:45) ~[spring-session-data-redis-3.2.1.jar!/:3.2.1]
	at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:229) ~[spring-session-core-3.2.1.jar!/:3.2.1]
	at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:145) ~[spring-session-core-3.2.1.jar!/:3.2.1]
	at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-3.2.1.jar!/:3.2.1]

import com.fasterxml.jackson.annotation.JsonTypeInfo;

public abstract class AjaxAuthenticationTokenMixin {

    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
    public abstract Object getCredentials();

    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
    public abstract Object getPrincipal();
 
}

private ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
mapper.addMixIn(AjaxAuthenticationToken.class, AjaxAuthenticationTokenMixin.class);
return mapper;
}


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: redis status: superseded An issue that has been superseded by another
Projects
Status: Done
Development

No branches or pull requests

3 participants