Implementing Request and Response Encryption in Spring Boot Using ControllerAdvice and Jackson

This article explains how to secure Spring Boot APIs by encrypting request and response bodies with symmetric AES, handling signatures, and configuring Jackson serialization to preserve enum and date formats, providing complete code examples and debugging insights.

Top Architect
Top Architect
Top Architect
Implementing Request and Response Encryption in Spring Boot Using ControllerAdvice and Jackson

In this article a senior architect demonstrates how to secure API interfaces in a Spring Boot backend by encrypting both request and response bodies.

The requirements include minimal impact on existing business logic, symmetric AES encryption, separate keys for H5 and mobile clients, and support for both GET and POST endpoints.

First, data models such as

@Data public class User { private Integer id; private String name; private UserType userType = UserType.COMMON; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime registerTime; }

and an enum

@Getter public enum UserType { VIP("VIP用户"), COMMON("普通用户"); private String code; private String type; UserType(String type) { this.code = name(); this.type = type; }

are defined.

A simple controller

@RestController @RequestMapping({"/user", "/secret/user"}) public class UserController { @RequestMapping("/list") ResponseEntity<List<User>> listUser() { List<User> users = new ArrayList<>(); User u = new User(); u.setId(1); u.setName("boyka"); u.setRegisterTime(LocalDateTime.now()); u.setUserType(UserType.COMMON); users.add(u); ResponseEntity<List<User>> response = new ResponseEntity<>(); response.setCode(200); response.setData(users); response.setMsg("用户列表查询成功"); return response; } }

returns a list of users.

To intercept and decrypt incoming requests, a

@ControllerAdvice @Order(Ordered.HIGHEST_PRECEDENCE) @Slf4j public class SecretRequestAdvice extends RequestBodyAdviceAdapter { @Override public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) { return true; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { String httpBody; if (Boolean.TRUE.equals(SecretFilter.secretThreadLocal.get())) { httpBody = decryptBody(inputMessage); } else { httpBody = StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset()); } return new SecretHttpMessage(new ByteArrayInputStream(httpBody.getBytes()), inputMessage.getHeaders()); } private String decryptBody(HttpInputMessage inputMessage) throws IOException { /* signature verification and AES decryption logic */ } }

is implemented, which reads the encrypted body, verifies signatures, and performs AES decryption.

For outgoing responses,

@ControllerAdvice public class SecretResponseAdvice implements ResponseBodyAdvice { private Logger logger = LoggerFactory.getLogger(SecretResponseAdvice.class); @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { Boolean respSecret = SecretFilter.secretThreadLocal.get(); String secretKey = SecretFilter.clientPrivateKeyThreadLocal.get(); SecretFilter.secretThreadLocal.remove(); SecretFilter.clientPrivateKeyThreadLocal.remove(); if (respSecret != null && respSecret) { if (o instanceof ResponseBasic) { if (SECRET_API_ERROR == ((ResponseBasic) o).getCode()) { return SecretResponseBasic.fail(((ResponseBasic) o).getCode(), ((ResponseBasic) o).getData(), ((ResponseBasic) o).getMsg()); } try { String data = EncryptUtils.aesEncrypt(JSON.toJSONString(o), secretKey); long timestamp = System.currentTimeMillis() / 1000; int salt = EncryptUtils.genSalt(); String newSignature = Md5Utils.genSignature(timestamp + "" + salt + "" + data + secretKey); return SecretResponseBasic.success(data, timestamp, salt, newSignature); } catch (Exception e) { logger.error("beforeBodyWrite error:", e); return SecretResponseBasic.fail(SECRET_API_ERROR, "", "服务端处理结果数据异常"); } } } return o; } }

encrypts the serialized JSON, adds a timestamp, salt and signature, and returns a standardized encrypted wrapper.

Initial testing shows correct encryption/decryption, but serialization issues appear: the enum and LocalDateTime fields are not formatted as expected when using FastJSON.

Switching to Jackson resolves the problem; the code is changed to String data = new ObjectMapper().writeValueAsString(o); and a custom ObjectMapper with LocalDateTimeSerializer and LocalDateTimeDeserializer is configured to use the pattern "yyyy-MM-dd HH:mm:ss":

String DATE_TIME_FORMATTER = "yyyy-MM-dd HH:mm:ss"; ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder() .findModulesViaServiceLoader(true) .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER))) .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER))) .build();

Finally, the SecretResponseAdvice is refactored to autowire the Spring‑managed ObjectMapper, ensuring that the encrypted response matches the format of the non‑encrypted version across all endpoints:

@Autowired private ObjectMapper objectMapper; ... String dataStr = objectMapper.writeValueAsString(o); String data = EncryptUtils.aesEncrypt(dataStr, secretKey);

The article also includes request/response examples, debugging steps, and references to related resources.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backend-developmentSpring BootJacksonencryptionControllerAdvice
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.