0
# JSON Serialization
1
2
Jackson module for serializing CAS authentication tokens and related objects in distributed session scenarios. This module enables proper JSON serialization and deserialization of CAS authentication objects for session management in clustered applications.
3
4
## Capabilities
5
6
### CAS Jackson2 Module
7
8
Jackson module that provides JSON serialization support for CAS authentication types, enabling distributed session management and caching scenarios.
9
10
```java { .api }
11
/**
12
* Jackson module for JSON serialization of CAS authentication objects.
13
* Registers mixins for proper serialization of CAS types in distributed environments.
14
*/
15
public class CasJackson2Module extends SimpleModule {
16
17
/**
18
* Creates CAS Jackson2 module with default configuration.
19
* Automatically registers mixins for CAS authentication types.
20
*/
21
public CasJackson2Module();
22
23
/**
24
* Sets up the module by registering mixins for CAS types.
25
* Called automatically by Jackson during module registration.
26
* @param context Jackson setup context for module configuration
27
*/
28
public void setupModule(SetupContext context);
29
}
30
```
31
32
**Usage Example:**
33
34
```java
35
@Configuration
36
public class JacksonConfig {
37
38
@Bean
39
@Primary
40
public ObjectMapper objectMapper() {
41
ObjectMapper mapper = new ObjectMapper();
42
mapper.registerModule(new CasJackson2Module());
43
return mapper;
44
}
45
}
46
```
47
48
## Jackson Mixin Classes
49
50
The module includes several Jackson mixin classes that handle the serialization complexity of CAS objects:
51
52
### CAS Authentication Token Mixin
53
54
Mixin for deserializing `CasAuthenticationToken` objects using Jackson.
55
56
```java { .api }
57
/**
58
* Mixin class for CasAuthenticationToken deserialization.
59
* Uses the private constructor designed for Jackson deserialization.
60
*/
61
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
62
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
63
isGetterVisibility = JsonAutoDetect.Visibility.NONE,
64
getterVisibility = JsonAutoDetect.Visibility.NONE,
65
creatorVisibility = JsonAutoDetect.Visibility.ANY)
66
@JsonIgnoreProperties(ignoreUnknown = true)
67
class CasAuthenticationTokenMixin {
68
69
/**
70
* Mixin constructor for deserializing CasAuthenticationToken.
71
* @param keyHash hashCode of the provider key for token validation
72
* @param principal the authenticated principal (usually username)
73
* @param credentials the service/proxy ticket ID from CAS
74
* @param authorities granted authorities for the user
75
* @param userDetails detailed user information
76
* @param assertion CAS assertion containing user attributes
77
*/
78
@JsonCreator
79
CasAuthenticationTokenMixin(
80
@JsonProperty("keyHash") Integer keyHash,
81
@JsonProperty("principal") Object principal,
82
@JsonProperty("credentials") Object credentials,
83
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
84
@JsonProperty("userDetails") UserDetails userDetails,
85
@JsonProperty("assertion") Assertion assertion);
86
}
87
```
88
89
### Assertion Implementation Mixin
90
91
Mixin for deserializing CAS `AssertionImpl` objects from the Apereo CAS client.
92
93
```java { .api }
94
/**
95
* Mixin for deserializing AssertionImpl from org.apereo.cas.client.validation package.
96
* Handles CAS assertion objects containing authentication details and user attributes.
97
*/
98
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
99
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
100
getterVisibility = JsonAutoDetect.Visibility.NONE,
101
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
102
@JsonIgnoreProperties(ignoreUnknown = true)
103
class AssertionImplMixin {
104
105
/**
106
* Mixin constructor for deserializing AssertionImpl.
107
* @param principal the Principal associated with the assertion
108
* @param validFromDate when the assertion becomes valid
109
* @param validUntilDate when the assertion expires
110
* @param authenticationDate when the user was authenticated
111
* @param attributes key/value pairs for assertion attributes
112
*/
113
@JsonCreator
114
AssertionImplMixin(
115
@JsonProperty("principal") AttributePrincipal principal,
116
@JsonProperty("validFromDate") Date validFromDate,
117
@JsonProperty("validUntilDate") Date validUntilDate,
118
@JsonProperty("authenticationDate") Date authenticationDate,
119
@JsonProperty("attributes") Map<String, Object> attributes);
120
}
121
```
122
123
### Attribute Principal Implementation Mixin
124
125
Mixin for deserializing CAS `AttributePrincipalImpl` objects from the Apereo CAS client.
126
127
```java { .api }
128
/**
129
* Mixin for deserializing AttributePrincipalImpl from org.apereo.cas.client.authentication package.
130
* Handles CAS principal objects containing user identity and attributes.
131
*/
132
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
133
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
134
getterVisibility = JsonAutoDetect.Visibility.NONE,
135
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
136
@JsonIgnoreProperties(ignoreUnknown = true)
137
class AttributePrincipalImplMixin {
138
139
/**
140
* Mixin constructor for deserializing AttributePrincipalImpl.
141
* @param name unique identifier for the principal (username)
142
* @param attributes key/value pairs for user attributes
143
* @param proxyGrantingTicket ticket for obtaining proxy tickets
144
* @param proxyRetriever callback implementation for CAS server communication
145
*/
146
@JsonCreator
147
AttributePrincipalImplMixin(
148
@JsonProperty("name") String name,
149
@JsonProperty("attributes") Map<String, Object> attributes,
150
@JsonProperty("proxyGrantingTicket") String proxyGrantingTicket,
151
@JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever);
152
}
153
```
154
155
## Serialization Support
156
157
The module provides serialization support for the following CAS types:
158
159
- **CasAuthenticationToken**: Complete authentication token with user details and CAS assertion
160
- **AssertionImpl**: CAS server assertion containing authentication details and attributes
161
- **AttributePrincipalImpl**: User principal with attributes from CAS server
162
163
### Usage Example
164
165
```java
166
// Example: Serializing CasAuthenticationToken
167
CasAuthenticationToken token = new CasAuthenticationToken(
168
"cas-key",
169
"username",
170
"ST-123456-abcdef",
171
authorities,
172
userDetails,
173
assertion
174
);
175
176
ObjectMapper mapper = new ObjectMapper();
177
mapper.registerModule(new CasJackson2Module());
178
179
// Serialize to JSON
180
String json = mapper.writeValueAsString(token);
181
182
// Deserialize from JSON
183
CasAuthenticationToken deserializedToken = mapper.readValue(json, CasAuthenticationToken.class);
184
```
185
186
## Configuration Examples
187
188
### Spring Boot Auto-Configuration
189
190
```java
191
@SpringBootApplication
192
public class Application {
193
194
@Bean
195
public Module casJackson2Module() {
196
return new CasJackson2Module();
197
}
198
199
public static void main(String[] args) {
200
SpringApplication.run(Application.class, args);
201
}
202
}
203
```
204
205
### Redis Session Configuration
206
207
```java
208
@Configuration
209
@EnableRedisHttpSession
210
public class RedisSessionConfig {
211
212
@Bean
213
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
214
ObjectMapper mapper = new ObjectMapper();
215
mapper.registerModule(new CasJackson2Module());
216
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
217
return new GenericJackson2JsonRedisSerializer(mapper);
218
}
219
}
220
```
221
222
### Hazelcast Session Configuration
223
224
```java
225
@Configuration
226
public class HazelcastSessionConfig {
227
228
@Bean
229
public HazelcastInstance hazelcastInstance() {
230
Config config = new Config();
231
232
// Configure serialization for CAS objects
233
SerializationConfig serializationConfig = config.getSerializationConfig();
234
serializationConfig.addSerializerConfig(
235
new SerializerConfig()
236
.setTypeClass(CasAuthenticationToken.class)
237
.setImplementation(new JsonSerializer())
238
);
239
240
return Hazelcast.newHazelcastInstance(config);
241
}
242
243
public static class JsonSerializer implements StreamSerializer<CasAuthenticationToken> {
244
private final ObjectMapper mapper;
245
246
public JsonSerializer() {
247
this.mapper = new ObjectMapper();
248
this.mapper.registerModule(new CasJackson2Module());
249
}
250
251
@Override
252
public void write(ObjectDataOutput out, CasAuthenticationToken token) throws IOException {
253
String json = mapper.writeValueAsString(token);
254
out.writeUTF(json);
255
}
256
257
@Override
258
public CasAuthenticationToken read(ObjectDataInput in) throws IOException {
259
String json = in.readUTF();
260
return mapper.readValue(json, CasAuthenticationToken.class);
261
}
262
263
@Override
264
public int getTypeId() {
265
return 1;
266
}
267
}
268
}
269
```
270
271
## Session Integration
272
273
### HTTP Session Serialization
274
275
```java
276
@Configuration
277
public class SessionConfig {
278
279
@Bean
280
public HttpSessionIdResolver httpSessionIdResolver() {
281
return HeaderHttpSessionIdResolver.xAuthToken();
282
}
283
284
@Bean
285
public SessionRepository<MapSession> sessionRepository() {
286
MapSessionRepository repository = new MapSessionRepository(new ConcurrentHashMap<>());
287
repository.setDefaultMaxInactiveInterval(Duration.ofMinutes(30));
288
return repository;
289
}
290
291
@Bean
292
public ObjectMapper sessionObjectMapper() {
293
ObjectMapper mapper = new ObjectMapper();
294
mapper.registerModule(new CasJackson2Module());
295
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
296
return mapper;
297
}
298
}
299
```
300
301
### Security Context Serialization
302
303
```java
304
@Configuration
305
public class SecurityContextConfig {
306
307
@Bean
308
public SecurityContextRepository securityContextRepository() {
309
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();
310
repository.setAllowSessionCreation(true);
311
return repository;
312
}
313
314
@Bean
315
public ObjectMapper securityContextObjectMapper() {
316
ObjectMapper mapper = new ObjectMapper();
317
mapper.registerModule(new CasJackson2Module());
318
mapper.registerModule(new CoreJackson2Module());
319
return mapper;
320
}
321
}
322
```
323
324
## Cache Integration
325
326
### Spring Cache with JSON Serialization
327
328
```java
329
@Configuration
330
@EnableCaching
331
public class CacheConfig {
332
333
@Bean
334
public CacheManager cacheManager() {
335
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
336
.serializeValuesWith(RedisSerializationContext.SerializationPair
337
.fromSerializer(casRedisSerializer()));
338
339
return RedisCacheManager.builder(redisConnectionFactory())
340
.cacheDefaults(config)
341
.build();
342
}
343
344
@Bean
345
public RedisSerializer<Object> casRedisSerializer() {
346
ObjectMapper mapper = new ObjectMapper();
347
mapper.registerModule(new CasJackson2Module());
348
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
349
return new GenericJackson2JsonRedisSerializer(mapper);
350
}
351
}
352
```
353
354
## Custom Serialization Configuration
355
356
### Custom Mixin Registration
357
358
```java
359
@Configuration
360
public class CustomJacksonConfig {
361
362
@Bean
363
public ObjectMapper customObjectMapper() {
364
ObjectMapper mapper = new ObjectMapper();
365
366
// Register CAS module
367
mapper.registerModule(new CasJackson2Module());
368
369
// Add custom mixins for additional types
370
mapper.addMixIn(CustomUserDetails.class, CustomUserDetailsMixin.class);
371
372
// Configure additional settings
373
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
374
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
375
376
return mapper;
377
}
378
379
@JsonIgnoreProperties(ignoreUnknown = true)
380
public static abstract class CustomUserDetailsMixin {
381
@JsonIgnore
382
abstract String getPassword();
383
}
384
}
385
```
386
387
## JSON Structure Examples
388
389
### Serialized CasAuthenticationToken
390
391
```json
392
{
393
"@class": "org.springframework.security.cas.authentication.CasAuthenticationToken",
394
"keyHash": 123456789,
395
"principal": "username",
396
"credentials": "ST-123456-abcdefghijklmnop",
397
"authorities": [
398
{
399
"@class": "org.springframework.security.core.authority.SimpleGrantedAuthority",
400
"authority": "ROLE_USER"
401
}
402
],
403
"userDetails": {
404
"@class": "org.springframework.security.core.userdetails.User",
405
"username": "username",
406
"authorities": [...],
407
"accountNonExpired": true,
408
"accountNonLocked": true,
409
"credentialsNonExpired": true,
410
"enabled": true
411
},
412
"assertion": {
413
"@class": "org.apereo.cas.client.validation.AssertionImpl",
414
"principal": {
415
"@class": "org.apereo.cas.client.authentication.AttributePrincipalImpl",
416
"name": "username",
417
"attributes": {
418
"email": "user@example.com",
419
"role": ["USER", "ADMIN"]
420
}
421
},
422
"validFromDate": "2023-10-01T10:00:00.000+00:00",
423
"validUntilDate": "2023-10-01T10:05:00.000+00:00",
424
"authenticationAttributes": {}
425
},
426
"authenticated": true
427
}
428
```
429
430
## Troubleshooting
431
432
### Common Serialization Issues
433
434
```java
435
// Issue: Circular reference during serialization
436
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
437
public class User implements UserDetails {
438
// ...
439
}
440
441
// Issue: Missing default constructor
442
public class CustomUserDetails implements UserDetails {
443
@JsonCreator
444
public CustomUserDetails(@JsonProperty("username") String username) {
445
this.username = username;
446
}
447
}
448
449
// Issue: Incompatible Jackson versions
450
@Configuration
451
public class JacksonVersionConfig {
452
@Bean
453
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
454
return new Jackson2ObjectMapperBuilder()
455
.modules(new CasJackson2Module())
456
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
457
}
458
}
459
```
460
461
## Security Considerations
462
463
- **Sensitive Data**: Avoid serializing passwords or sensitive credentials
464
- **Type Safety**: Use `@JsonTypeInfo` for secure polymorphic deserialization
465
- **Validation**: Validate deserialized objects before use
466
- **Version Compatibility**: Ensure Jackson version compatibility across services
467
468
## Integration Notes
469
470
- Register module before any CAS authentication occurs
471
- Compatible with Spring Session and Spring Security Context persistence
472
- Supports Redis, Hazelcast, and other distributed cache providers
473
- Required for clustered applications using CAS authentication
474
- Test serialization/deserialization in development environment