Implementing API Request and Response Encryption/Decryption with Spring Boot and ControllerAdvice
This article walks through the design and implementation of symmetric AES encryption for both GET and POST API endpoints in a Spring Boot application, covering requirement analysis, data model definition, custom RequestBodyAdvice and ResponseBodyAdvice, serialization challenges with FastJSON vs Jackson, and final configuration to ensure consistent JSON output across encrypted and non‑encrypted responses.
The article starts by outlining the need for API security, requiring minimal impact on existing business logic, symmetric encryption for Android, iOS, and H5 clients, and support for both GET and POST methods.
Data models are defined using Lombok and Jackson annotations:
@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;
}
@Getter
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum UserType {
VIP("VIP用户"),
COMMON("普通用户");
private String code;
private String type;
UserType(String type) { this.code = name(); this.type = type; }
}A simple controller provides a user list endpoint:
@RestController
@RequestMapping({"/user", "/secret/user"})
public class UserController {
@RequestMapping("/list")
ResponseEntity
> listUser() {
// build and return a sample user list
}
}Two ControllerAdvice classes intercept request and response bodies. The request advice decrypts incoming data, validates signatures, and extracts the original JSON; the response advice encrypts the output, adds a timestamp, salt, and signature.
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecretRequestAdvice extends RequestBodyAdviceAdapter {
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class
> aClass) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class
> converterType) throws IOException {
// decrypt logic …
}
private String decryptBody(HttpInputMessage inputMessage) throws IOException { /* … */ }
}
@ControllerAdvice
public class SecretResponseAdvice implements ResponseBodyAdvice
{
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) { return true; }
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest request, ServerHttpResponse response) {
// serialize with Jackson, encrypt, add signature
}
}During testing, serialization inconsistencies appeared: FastJSON produced enum and LocalDateTime representations that differed from the non‑encrypted version. Switching to Jackson’s ObjectMapper resolved enum formatting, but LocalDateTime was output as a full object.
To restore the original date format, a custom ObjectMapper configuration was added:
String DATE_TIME_FORMATTER = "yyyy-MM-dd HH:mm:ss";
ObjectMapper mapper = new Jackson2ObjectMapperBuilder()
.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER)))
.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER)))
.build();After injecting this mapper into SecretResponseAdvice , the encrypted response matched the plain response exactly, including enum fields and formatted timestamps. The article concludes with a note that further edge cases (different date patterns, cross‑origin issues) may still need attention.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.