springsession全能序列化方案

java 复制代码
package com.kongjs.common.session.config;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.kongjs.common.session.jackson.RestJacksonModule;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.JacksonJsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.security.jackson.SecurityJacksonModule;
import org.springframework.security.jackson.SecurityJacksonModules;
import tools.jackson.databind.DefaultTyping;
import tools.jackson.databind.JacksonModule;
import tools.jackson.databind.cfg.MapperBuilder;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import tools.jackson.databind.module.SimpleModule;

import java.util.ArrayList;
import java.util.List;


@Configuration(proxyBeanMethods = false)
public class SessionConfig implements BeanClassLoaderAware {

    private ClassLoader beanClassloader;

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new JacksonJsonRedisSerializer<>(objectMapper(), Object.class);
    }

    private JsonMapper objectMapper() {
        List<JacksonModule> modules = SecurityJacksonModules.getModules(this.beanClassloader);
        RestJacksonModule restJacksonModule = new RestJacksonModule();
        List<JacksonModule> restModules = new ArrayList<>(modules);
        restModules.add(restJacksonModule);
        applyPolymorphicTypeValidator(restModules, null);
        return JsonMapper.builder().addModules(restModules).build();
    }
    // 放开某些类型
    private static void applyPolymorphicTypeValidator(List<JacksonModule> modules, BasicPolymorphicTypeValidator.Builder typeValidatorBuilder) {
        BasicPolymorphicTypeValidator.Builder builder = (typeValidatorBuilder != null) ? typeValidatorBuilder
                : BasicPolymorphicTypeValidator.builder();
        for (JacksonModule module : modules) {
            if (module instanceof SecurityJacksonModule securityModule) {
                securityModule.configurePolymorphicTypeValidator(builder);
            }
        }
        modules.add(new SimpleModule() {
            @Override
            public void setupModule(SetupContext context) {
                ((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(),
                        DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            }
        });
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassloader = classLoader;
    }
}
java 复制代码
package com.kongjs.common.session.jackson;


import com.kongjs.common.session.authentication.RestAuthentication;
import com.kongjs.common.session.dto.AccountInfo;
import org.springframework.security.jackson.SecurityJacksonModule;
import tools.jackson.core.Version;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;

import java.util.LinkedHashMap;
// 使用这个方便
public class RestJacksonModule extends SecurityJacksonModule {

    public RestJacksonModule() {
        super(RestJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
    }

    public RestJacksonModule(String name, Version version) {
        super(name, version);
    }

    @Override
    public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
        builder.allowIfBaseType(Object.class); // 允许 Object 所有子类型(包含 Long/Integer 等)
        builder.allowIfBaseType(Long.class);   // 显式允许 Long 类型
        builder.allowIfBaseType(LinkedHashMap.class); // 兼容认证信息中的 Map 类型
        builder.allowIfSubType(RestAuthentication.class);
        builder.allowIfSubType(AccountInfo.class);
    }

    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.setMixIn(RestAuthentication.class, RestAuthenticationMixin.class);
        context.setMixIn(AccountInfo.class, AccountInfoMixin.class);
    }
}
java 复制代码
package com.kongjs.common.session.authentication;

import com.fasterxml.jackson.annotation.JsonInclude;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

import java.io.Serial;
import java.util.Collection;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestAuthentication extends AbstractAuthenticationToken {

    @Serial
    private static final long serialVersionUID = 20260313L;
    private final Object principal;
    private Object credentials;

    public RestAuthentication(Object principal, Object credentials) {
        super((Collection<? extends GrantedAuthority>) null);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(false);
    }

    public RestAuthentication(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }


    public static RestAuthentication unauthenticated(Object principal, Object credentials) {
        return new RestAuthentication(principal, credentials);
    }

    public static RestAuthentication authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        return new RestAuthentication(principal, credentials, authorities);
    }

    protected RestAuthentication(Builder<?> builder) {
        super(builder);
        this.principal = builder.principal;
        this.credentials = builder.credentials;
    }

    @Override
    public @Nullable Object getPrincipal() {
        return principal;
    }

    @Override
    public @Nullable Object getCredentials() {
        return credentials;
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }

    public Builder<?> toBuilder() {
        return new Builder<>(this);
    }

    public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {
        private @Nullable Object principal;
        private @Nullable Object credentials;

        protected Builder(RestAuthentication token) {
            super(token);
            this.principal = token.principal;
            this.credentials = token.credentials;
        }

        public B principal(@Nullable Object principal) {
            Assert.notNull(principal, "principal cannot be null");
            this.principal = principal;
            return (B) this;
        }

        public B credentials(@Nullable Object credentials) {
            this.credentials = credentials;
            return (B) this;
        }

        public RestAuthentication build() {
            return new RestAuthentication(this);
        }
    }
}
package com.kongjs.common.session.jackson;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import tools.jackson.databind.annotation.JsonDeserialize;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonDeserialize(using = RestAuthenticationDeserializer.class)
public class RestAuthenticationMixin {
}
package com.kongjs.common.session.jackson;


import com.kongjs.common.session.authentication.RestAuthentication;
import org.jspecify.annotations.Nullable;
import org.springframework.security.core.GrantedAuthority;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonParser;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.DatabindException;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ValueDeserializer;
import tools.jackson.databind.node.MissingNode;

import java.util.List;

public class RestAuthenticationDeserializer extends ValueDeserializer<RestAuthentication> {
    private static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {
    };

    /**
     * This method construct {@link RestAuthentication} object from
     * serialized json.
     *
     * @param jp   the JsonParser
     * @param ctxt the DeserializationContext
     * @return the user
     * @throws JacksonException if an error during JSON processing occurs
     */
    @Override
    public RestAuthentication deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException {
        JsonNode jsonNode = ctxt.readTree(jp);
        boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
        JsonNode principalNode = readJsonNode(jsonNode, "principal");
        Object principal = getPrincipal(ctxt, principalNode);
        JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
        Object credentials = getCredentials(credentialsNode);
        JsonNode authoritiesNode = readJsonNode(jsonNode, "authorities");
        List<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,
                ctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_LIST));
        RestAuthentication token = (!authenticated)
                ? RestAuthentication.unauthenticated(principal, credentials)
                : RestAuthentication.authenticated(principal, credentials, authorities);
        JsonNode detailsNode = readJsonNode(jsonNode, "details");
        if (detailsNode.isNull() || detailsNode.isMissingNode()) {
            token.setDetails(null);
        } else {
            Object details = ctxt.readTreeAsValue(detailsNode, Object.class);
            token.setDetails(details);
        }
        return token;
    }

    private Object getPrincipal(DeserializationContext ctxt, JsonNode principalNode) throws StreamReadException, DatabindException {
        if (principalNode.isObject()) {
            return ctxt.readTreeAsValue(principalNode, Object.class);
        }
        return principalNode.asString();
    }

    private @Nullable Object getCredentials(JsonNode credentialsNode) {
        if (credentialsNode.isNull() || credentialsNode.isMissingNode()) {
            return null;
        }
        return credentialsNode.asString();
    }

    private JsonNode readJsonNode(JsonNode jsonNode, String field) {
        return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
    }
}
相关推荐
JWASX2 小时前
【RocketMQ 生产者和消费者】- 事务源码分析(1)
java·rocketmq·java-rocketmq
其实防守也摸鱼2 小时前
CTF密码学综合教学指南--第九章
开发语言·网络·python·安全·网络安全·密码学·ctf
砚底藏山河2 小时前
Python量化开发:2026最佳实时股票数据API接口推荐与对比
开发语言·windows·python
AlunYegeer3 小时前
JAVA,以后端的视角理解前端。在全栈的路上迈出第一步。
java·开发语言·前端
hixiong1233 小时前
C# OpenvinoSharp使用DINOv2模型进行图像相似度计算
开发语言·c#
DFT计算杂谈4 小时前
自动化脚本一键绘制三元化合物相图
java·运维·服务器·开发语言·前端·python·自动化
2301_771717214 小时前
Spring Boot 自动配置核心注解
java·spring boot·mybatis
小Y._4 小时前
面试被问synchronized锁升级,这5个问题答不上来直接挂!
java
EW Frontier4 小时前
6G ISAC新范式:基于智能漏波天线的Wi‑Fi通感一体化系统设计与实测【附MATLAB+python代码】
开发语言·python·matlab·music·isac·doa·wi‑fi
姚青&4 小时前
测试技术体系
java·python