使用自定义用户对象时的 Spring 会话 Redis

AKr*_*h95 7 java spring redis spring-boot spring-session

我正在按照本教程将 spring 会话(通过 redis)添加到我的应用程序中。http://www.baeldung.com/spring-session

我添加依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

和 SessionConfig.java 类

@Configuration
@EnableRedisHttpSession
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
}
Run Code Online (Sandbox Code Playgroud)

我已经在一个空白项目中完成了此操作,并且效果很好。但我当前的项目有一些带有自定义 UserDetailsS​​ervice 的自定义用户对象,这就是出现问题的地方。

Sat Apr 07 11:21:49 EDT 2018
There was an unexpected error (type=Internal Server Error, status=500).
Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.kreyzon.dollar.entity.User
Run Code Online (Sandbox Code Playgroud)

现在,我环顾四周并找到了实现此目的的建议:

@Bean
public RedisTemplate<Object, Object> sessionRedisTemplate (
        RedisConnectionFactory connectionFactory) {
    RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());

    template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());

    template.setConnectionFactory(connectionFactory);
    return template;
}
Run Code Online (Sandbox Code Playgroud)

那不起作用(好像甚至没有被识别)。我也尝试过这个

@Bean(name = {"defaultRedisSerializer", "springSessionDefaultRedisSerializer"})
RedisSerializer<Object> defaultRedisSerializer() {
    return new GenericJackson2JsonRedisSerializer();
}
Run Code Online (Sandbox Code Playgroud)

现在,这实际上做了一些事情,但仍然导致错误

Sat Apr 07 11:39:21 EDT 2018
There was an unexpected error (type=Internal Server Error, status=500).
Could not read JSON: Can not construct instance of org.springframework.security.authentication.UsernamePasswordAuthenticationToken: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: [B@4dc133; line: 1, column: 184] (through reference chain: org.springframework.security.core.context.SecurityContextImpl["authentication"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.springframework.security.authentication.UsernamePasswordAuthenticationToken: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: [B@4dc133; line: 1, column: 184] (through reference chain: org.springframework.security.core.context.SecurityContextImpl["authentication"])
Run Code Online (Sandbox Code Playgroud)

我还探索了其他序列化器,例如Jackson2JsonRedisSerializerGenericJackson2JsonRedisSerializer但遇到了相同的错误..

感谢所有帮助,谢谢。

更新:

我也尝试过添加这个自定义序列化器

public class CustomRedisSerializer implements RedisSerializer<Object> {

    private Converter<Object, byte[]> serializer = new SerializingConverter();
    private Converter<byte[], Object> deserializer = new DeserializingConverter();

    static final byte[] EMPTY_ARRAY = new byte[0];

    public Object deserialize(byte[] bytes) {
        if (isEmpty(bytes)) {
            return null;
        }

        try {
            return deserializer.convert(bytes);
        } catch (Exception ex) {
            throw new SerializationException("Cannot deserialize", ex);
        }
    }

    public byte[] serialize(Object object) {
        if (object == null) {
            return EMPTY_ARRAY;
        }

        try {
            return serializer.convert(object);
        } catch (Exception ex) {
            return EMPTY_ARRAY;
            //TODO add logic here to only return EMPTY_ARRAY for known conditions
            // else throw the SerializationException
            // throw new SerializationException("Cannot serialize", ex);
        }
    }

    private boolean isEmpty(byte[] data) {
        return (data == null || data.length == 0);
    }
}
Run Code Online (Sandbox Code Playgroud)

第二次更新:

所以我添加implements Serializable到我所有的自定义对象中。那行得通,去算吧!(出于某种原因,我发现没有一篇关于该主题的帖子表明这一点)

现在,我的最后一个问题仍然存在。我使用 LDAP 进行身份验证,如果身份验证失败,它会返回一个LdapCtx不可序列化的对象并引发错误

Sat Apr 07 12:35:14 EDT 2018
There was an unexpected error (type=Internal Server Error, status=500).
Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.sun.jndi.ldap.LdapCtx
Run Code Online (Sandbox Code Playgroud)

有没有办法强制这个类可序列化?

更新(2018 年 10 月 22 日):

此问题已解决 https://github.com/spring-projects/spring-security/issues/5378

Ved*_*vic 11

如果您使用的序列化机制依赖于标准 Java 序列化,例如JdkSerializationRedisSerializer,则必须确保存储在会话中的所有内容都实现Serializable. 正如您自己所经历的,这对于属于项目一部分的类来说相当简单,但对于第 3 方类来说可能是一个挑战。

我建议您仔细研究基于 JSON 的序列化,即Jackson2JsonRedisSerializer。Spring Session 代码库中有一个示例应用程序演示了这种方法。

此特定示例用于SecurityJackson2Modules序列化 Spring Security 的对象。为了能够使用基于 Jackson 的 JSON 序列化来序列化您的第 3 方类,您应该执行类似于 Spring Security 提供的操作。