docs
Spring Security provides comprehensive Jackson support for serializing and deserializing security-related objects. Spring Security 7.0+ uses Jackson 3, while earlier versions used Jackson 2 (now deprecated).
Core Capabilities:
SecurityJacksonModules.getModules()CoreJacksonModule registers mixins and type validation for core security typesBasicPolymorphicTypeValidator for secure deserializationKey Interfaces and Classes:
SecurityJacksonModules - Utility: getModules(ClassLoader) returns list of modulesSecurityJacksonModule - Abstract base class for security modulesCoreJacksonModule - Module for core security types (Authentication, UserDetails, etc.)SimpleGrantedAuthorityMixin, UserMixin, UsernamePasswordAuthenticationTokenMixin, etc.Default Behaviors:
SecurityJacksonModules.getModules() (discovers all available modules)BasicPolymorphicTypeValidator for secure deserialization@JsonTypeInfo on mixins (type info stored in JSON)JsonAutoDetect with field visibility (getters hidden)Threading Model:
Lifecycle:
Exceptions:
JsonProcessingException (handle in try/catch)JsonMappingException (invalid JSON or type)Edge Cases:
SecurityJacksonModule@JsonTypeInfo for type information in JSONUtility class for registering Spring Security Jackson modules.
public final class SecurityJacksonModules { .api }Description: Factory for obtaining Jackson 3 modules for Spring Security types.
Static Methods:
public static List<JacksonModule> getModules(ClassLoader classLoader) { .api }classLoader - ClassLoader for discovering modulespublic static List<JacksonModule> getModules(
ClassLoader classLoader,
BasicPolymorphicTypeValidator.Builder validatorBuilder) { .api }classLoader - ClassLoader for module discoveryvalidatorBuilder - Builder for configuring allowed typesPackage: org.springframework.security.jackson
Example:
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Register Spring Security modules
List<JacksonModule> modules =
SecurityJacksonModules.getModules(getClass().getClassLoader());
modules.forEach(mapper::registerModule);
return mapper;
}
}Abstract base class for Spring Security Jackson modules.
public abstract class SecurityJacksonModule extends JacksonModule { .api }Description: Base class providing polymorphic type validation configuration.
Abstract Methods:
public abstract void configurePolymorphicTypeValidator(
BasicPolymorphicTypeValidator.Builder builder) { .api }builder - Builder for configuring type validationPackage: org.springframework.security.jackson
Jackson module for core Spring Security types.
public class CoreJacksonModule extends SecurityJacksonModule { .api }Description: Registers mixins and type validation for core security classes.
Supported Types:
SimpleGrantedAuthorityFactorGrantedAuthorityAnonymousAuthenticationTokenRememberMeAuthenticationTokenUsernamePasswordAuthenticationTokenTestingAuthenticationTokenUser (UserDetails implementation)SecurityContextImplBadCredentialsExceptionInstant, Duration)Package: org.springframework.security.jackson
Example:
@Bean
public ObjectMapper securityObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new CoreJacksonModule());
return mapper;
}Serialize and deserialize Authentication objects:
@Service
public class AuthenticationSerializationService {
private final ObjectMapper objectMapper;
public AuthenticationSerializationService() {
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new CoreJacksonModule());
// Enable default typing for polymorphic types
BasicPolymorphicTypeValidator validator =
BasicPolymorphicTypeValidator.builder()
.allowIfSubType("org.springframework.security")
.build();
objectMapper.activateDefaultTyping(
validator,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
}
public String serialize(Authentication authentication) throws Exception {
return objectMapper.writeValueAsString(authentication);
}
public Authentication deserialize(String json) throws Exception {
return objectMapper.readValue(json, Authentication.class);
}
}Example JSON:
{
"@class": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
"principal": {
"@class": "org.springframework.security.core.userdetails.User",
"username": "user",
"password": "{noop}password",
"enabled": true,
"accountNonExpired": true,
"credentialsNonExpired": true,
"accountNonLocked": true,
"authorities": [
"java.util.Collections$UnmodifiableSet",
[
{
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
"authority": "ROLE_USER"
}
]
]
},
"credentials": "{noop}password",
"authenticated": true,
"authorities": [
"java.util.Collections$UnmodifiableSet",
[
{
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
"authority": "ROLE_USER"
}
]
]
}Serialize SecurityContext for session storage:
@Component
public class SecurityContextSerializer {
private final ObjectMapper objectMapper;
public SecurityContextSerializer() {
this.objectMapper = JsonMapper.builder()
.findAndAddModules()
.build();
// Register Spring Security modules
SecurityJacksonModules.getModules(getClass().getClassLoader())
.forEach(objectMapper::registerModule);
// Configure type validation
BasicPolymorphicTypeValidator validator =
BasicPolymorphicTypeValidator.builder()
.allowIfSubType("org.springframework.security")
.allowIfSubType("java.util")
.allowIfSubType("java.time")
.build();
objectMapper.activateDefaultTyping(
validator,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
}
public byte[] serialize(SecurityContext context) {
try {
return objectMapper.writeValueAsBytes(context);
} catch (Exception e) {
throw new RuntimeException("Failed to serialize SecurityContext", e);
}
}
public SecurityContext deserialize(byte[] data) {
try {
return objectMapper.readValue(data, SecurityContext.class);
} catch (Exception e) {
throw new RuntimeException(
"Failed to deserialize SecurityContext", e
);
}
}
}Serialize UserDetails for caching or session storage:
public class UserDetailsSerializer {
private final ObjectMapper objectMapper;
public UserDetailsSerializer() {
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new CoreJacksonModule());
}
public String serialize(UserDetails userDetails) throws Exception {
return objectMapper.writeValueAsString(userDetails);
}
public UserDetails deserialize(String json) throws Exception {
return objectMapper.readValue(json, User.class);
}
}Example JSON:
{
"@class": "org.springframework.security.core.userdetails.User",
"username": "admin",
"password": "{bcrypt}$2a$10$...",
"enabled": true,
"accountNonExpired": true,
"credentialsNonExpired": true,
"accountNonLocked": true,
"authorities": [
"java.util.Collections$UnmodifiableSet",
[
{
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
"authority": "ROLE_ADMIN"
},
{
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
"authority": "ROLE_USER"
}
]
]
}Spring Security uses Jackson mixins to control serialization:
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) { .api }
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE
)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class SimpleGrantedAuthorityMixin { .api }Package: org.springframework.security.jackson
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) { .api }
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE
)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class UserMixin { .api }Constructor Binding:
@JsonCreator
UserMixin(
@JsonProperty("username") String username,
@JsonProperty("password") String password,
@JsonProperty("enabled") boolean enabled,
@JsonProperty("accountNonExpired") boolean accountNonExpired,
@JsonProperty("credentialsNonExpired") boolean credentialsNonExpired,
@JsonProperty("accountNonLocked") boolean accountNonLocked,
@JsonProperty("authorities") Set<? extends GrantedAuthority> authorities
)@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) { .api }
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE
)
@JsonIgnoreProperties(ignoreUnknown = true)
abstract class UsernamePasswordAuthenticationTokenMixin { .api }Customize allowed types for deserialization:
@Configuration
public class CustomJacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Register Security modules
List<JacksonModule> modules =
SecurityJacksonModules.getModules(getClass().getClassLoader());
modules.forEach(mapper::registerModule);
// Custom type validator
BasicPolymorphicTypeValidator validator =
BasicPolymorphicTypeValidator.builder()
// Allow Spring Security types
.allowIfSubType("org.springframework.security")
// Allow custom types
.allowIfSubType("com.example.security")
// Allow specific classes
.allowIfBaseType(Authentication.class)
.allowIfBaseType(GrantedAuthority.class)
// Allow Java standard types
.allowIfSubType("java.util")
.allowIfSubType("java.time")
.build();
mapper.activateDefaultTyping(
validator,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
return mapper;
}
}Create custom Jackson module for application-specific types:
public class CustomSecurityJacksonModule extends SecurityJacksonModule {
@Override
public void setupModule(SetupContext context) {
// Register custom mixins
context.setMixInAnnotations(
CustomAuthenticationToken.class,
CustomAuthenticationTokenMixin.class
);
context.setMixInAnnotations(
CustomUserDetails.class,
CustomUserDetailsMixin.class
);
}
@Override
public void configurePolymorphicTypeValidator(
BasicPolymorphicTypeValidator.Builder builder) {
// Allow custom types
builder.allowIfSubType("com.example.security");
}
}
// Custom mixin
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
abstract class CustomAuthenticationTokenMixin {
@JsonCreator
CustomAuthenticationTokenMixin(
@JsonProperty("principal") Object principal,
@JsonProperty("credentials") Object credentials,
@JsonProperty("customData") Map<String, Object> customData
) { }
@JsonProperty("customData")
abstract Map<String, Object> getCustomData();
}
// Registration
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new CoreJacksonModule());
mapper.registerModule(new CustomSecurityJacksonModule());
return mapper;
}Configure Jackson for Redis session storage:
@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer(objectMapper());
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Register Spring Security modules
SecurityJacksonModules.getModules(getClass().getClassLoader())
.forEach(mapper::registerModule);
// Configure type validation
BasicPolymorphicTypeValidator validator =
BasicPolymorphicTypeValidator.builder()
.allowIfSubType("org.springframework.security")
.allowIfSubType("java.util")
.allowIfSubType("java.time")
.build();
mapper.activateDefaultTyping(
validator,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);
// Additional configuration
mapper.configure(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
false
);
mapper.registerModule(new JavaTimeModule());
return mapper;
}
}Store authentication in database as JSON:
@Entity
@Table(name = "user_sessions")
public class UserSession {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "session_id", unique = true)
private String sessionId;
@Column(name = "authentication_json", columnDefinition = "TEXT")
private String authenticationJson;
@Column(name = "created_at")
private Instant createdAt;
@Column(name = "expires_at")
private Instant expiresAt;
// Getters and setters
}
@Service
public class SessionService {
private final UserSessionRepository repository;
private final ObjectMapper objectMapper;
public SessionService(UserSessionRepository repository) {
this.repository = repository;
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new CoreJacksonModule());
}
public void saveSession(String sessionId, Authentication authentication) {
try {
String json = objectMapper.writeValueAsString(authentication);
UserSession session = new UserSession();
session.setSessionId(sessionId);
session.setAuthenticationJson(json);
session.setCreatedAt(Instant.now());
session.setExpiresAt(Instant.now().plus(Duration.ofHours(1)));
repository.save(session);
} catch (Exception e) {
throw new RuntimeException("Failed to save session", e);
}
}
public Authentication loadSession(String sessionId) {
return repository.findBySessionId(sessionId)
.map(session -> {
try {
return objectMapper.readValue(
session.getAuthenticationJson(),
Authentication.class
);
} catch (Exception e) {
throw new RuntimeException("Failed to load session", e);
}
})
.orElse(null);
}
}// Configure strict type validation
BasicPolymorphicTypeValidator validator =
BasicPolymorphicTypeValidator.builder()
// Whitelist specific packages
.allowIfSubType("org.springframework.security")
// Deny dangerous types
.denyForExactBaseType(Object.class)
.build();
mapper.activateDefaultTyping(
validator,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY
);public class SecureUserDetails extends User {
@JsonIgnore
private String sensitiveData;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
// Other fields and methods
}Spring Security 7.0 deprecated Jackson 2 support:
@Deprecated(since = "7.0") { .api }
public final class SecurityJackson2Modules {
@Deprecated(since = "7.0")
public static void enableDefaultTyping(ObjectMapper mapper) { .api }
@Deprecated(since = "7.0")
public static List<Module> getModules(ClassLoader classLoader) { .api }
}
@Deprecated(since = "7.0")
public class CoreJackson2Module extends SimpleModule { .api }Migration Path:
// Old (deprecated)
ObjectMapper mapper = new ObjectMapper();
SecurityJackson2Modules.enableDefaultTyping(mapper);
// New (Jackson 3)
ObjectMapper mapper = new ObjectMapper();
SecurityJacksonModules.getModules(getClass().getClassLoader())
.forEach(mapper::registerModule);