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();
}
}