Global API Encryption with @ControllerAdvice: 30-Min Spring Boot Implementation & Benchmarks

This tutorial demonstrates how to implement global API encryption in Spring Boot using @ControllerAdvice and ResponseBodyAdvice with AES, covering step-by-step code, three critical pitfalls to avoid, and performance benchmarks showing AES adds only ~6ms overhead per request.

Engineer Tang's Implementation Study Notes
Engineer Tang's Implementation Study Notes
Engineer Tang's Implementation Study Notes
Global API Encryption with @ControllerAdvice: 30-Min Spring Boot Implementation & Benchmarks

Background: Why Interface Encryption Matters

The Kuaishou security incident highlighted how AI-driven automated attacks can bypass authentication and exploit vulnerabilities at scale. For backend developers, APIs are the core data-exchange channel; transmitting sensitive data (phone numbers, passwords, order details) in plaintext is equivalent to handing attackers a ready-to-use key. Encryption ensures data confidentiality and integrity during transit.

Solution Overview: @ControllerAdvice + ResponseBodyAdvice

The combination of @ControllerAdvice and ResponseBodyAdvice provides a lightweight, non-invasive way to encrypt all responses globally without modifying any controller code. @ControllerAdvice defines the scope (e.g., basePackages = "com.example.controller"), while ResponseBodyAdvice intercepts the response body before serialization to apply encryption.

Step-by-Step Implementation

1. Core Annotations

@Component
@ControllerAdvice(basePackages = "com.example.controller")
@Slf4j
public class EncodeResponseBodyAdvice implements ResponseBodyAdvice<Object> { ... }
@Component

: Registers the class as a Spring bean so the advice is activated. @ControllerAdvice: Restricts the advice to controllers under the specified package, avoiding unnecessary encryption of public endpoints. @Slf4j: Provides a logger for debugging encryption/decryption issues.

2. The supports Method: Encryption Switch

@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    return true; // "one-size-fits-all" – encrypt every response in scope
}

Returning true encrypts all responses in the advised package. The article notes this is simple but may add overhead to public endpoints. Advanced usage: check for a custom @NeedEncrypt annotation on the method to encrypt selectively.

3. Core Logic in beforeBodyWrite

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
        MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 1. Log original data for troubleshooting
    log.info("【原始数据】准备加密: {}", body);
    
    // 2. Normalize to unified ResultEntity
    ResultEntity result = JSON.parseObject(JSON.toJSONString(body), ResultEntity.class);
    
    // 3. Skip encryption for null data
    if (ObjectUtil.isNull(result) || ObjectUtil.isNull(result.getData())) {
        return result.ok();
    }
    
    // 4. Encrypt data field, destroy plaintext
    String encryptedData = AesUtils.encrypt(result.getData().toString(), getSecretKey());
    result.setData(null);
    result.setEncrypt_data(encryptedData);
    
    return result;
}

The method logs the raw response, converts any return type into a standard ResultEntity (containing code, msg, data, encrypt_data), skips null payloads, encrypts the data field with AES, nulls out the plaintext, and returns the wrapper with only encrypt_data populated.

4. Example Transformation

Plaintext response: {"code":200,"msg":"成功","data":"用户密码123"} Encrypted response: {"code":200,"msg":"成功","encrypt_data":"x9#kL2!pQ7*zR3"} Interceptors see only ciphertext.

Three Critical Pitfalls

Pitfall 1: Hardcoding the Secret Key

// ❌ Wrong
String key = "test@2025";

// ✅ Correct
@Value("${aes.secret-key}")
private String secretKey;

Keys must live in configuration files (e.g., application.yml) for rotation and to avoid leaking secrets in version control.

Pitfall 2: Non-Standard ResultEntity

The wrapper class must have exactly these fields with getters/setters:

public class ResultEntity {
    private Integer code;
    private String msg;
    private Object data;
    private String encrypt_data; // encrypted payload
}

Pitfall 3: Forgetting Frontend Coordination

Encryption is bidirectional. The frontend must know:

Algorithm (AES)

Secret key (from config)

Decryption logic

Otherwise the client cannot decode responses, leading to wasted debugging time.

Performance Benchmarks (Real-World Tests)

No encryption: ~3 ms baseline

AES encryption (single call): ~6 ms – negligible impact

RSA encryption (single call): ~45 ms – acceptable for low-frequency calls

Full-interface blanket encryption: +30% latency – needs optimization (use selective encryption via supports or custom annotation)

Conclusion: AES overhead is virtually unnoticeable; RSA is heavier but usable for specific high-security endpoints. Blanket encryption should be avoided for performance-sensitive public APIs.

Summary & Homework

@ControllerAdvice

scopes the encryption; ResponseBodyAdvice performs it. Together they deliver global, zero-intrusion encryption. The article ends with a challenge: implement selective encryption using a custom annotation (e.g., @NeedEncrypt) combined with @ControllerAdvice so that only annotated endpoints are encrypted, leaving public endpoints untouched.

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.

performance benchmarkSpring BootHutoolAPI SecurityAES encryptionJava backend@ControllerAdviceResponseBodyAdvice
Engineer Tang's Implementation Study Notes
Written by

Engineer Tang's Implementation Study Notes

Old Tang | Pseudo-full-stack tech player: writes code without going bald, manages projects without going crazy, delivers without staying up late. No formal tutorials here, only a "slacking-off" practical manual that cures system pain points. Focuses on Shell, SQL, Java, and delivery, turning complexity into simplicity and making efficiency fun.

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.