Spring Boot AOP: Transparent Request/Response Encryption Example

Overview

Aspect-Oriented Programming enables modular handling of cross-cutting concerns. This guide demonstrates implementing automatic encryption and decryption for controller layer data using Spring Boot AOP, allowing business logic to remain clean while security concerns are handled transparently.

Core Concepts

AOP terminology includes:

  • Aspect: A module encapsulating cross-cutting behavior (e.g., security, logging)
  • Join Point: Specific execution points where aspects apply (method invocasions, exceptions)
  • Advice: Actions executed at particular join points, categorized as:
    • @Before: Executes before method invocation
    • @After: Executes after method completion (regardless of outcome)
    • @AfterReturning: Executes after successful method return
    • @AfterThrowing: Executes when methods throw exceptions
    • @Around: Wraps method execution, controlling invocation timing
  • Pointcut: Expressions defining which join points trigger advice

Environment Configuration

Maven Dependencies

<properties>
    <java.version>1.8</java.version>
    <spring-boot.version>2.2.6.RELEASE</spring-boot.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.68</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Application Properties

server.port=8080
spring.application.name=aop-encryption-demo
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true

Implementation

Data Encryption Aspect

Create an aspect class to intercept controller methods:

@Aspect
@Component
public class DataSecurityAspect {

    @Pointcut("execution(public * com.example.app.api.*.*(..))")
    public void controllerMethods() {}

    @Before("controllerMethods()")
    public void decryptRequestPayload(JoinPoint joinPoint) {
        Arrays.stream(joinPoint.getArgs())
              .filter(Account.class::isInstance)
              .map(Account.class::cast)
              .forEach(account -> {
                  try {
                      String decryptedUsername = decodeBase64(account.getUsername());
                      account.setUsername(decryptedUsername);
                  } catch (Exception ex) {
                      throw new RuntimeException("Decryption failed", ex);
                  }
              });
    }

    @AfterReturning(pointcut = "controllerMethods()", returning = "response")
    public void encryptResponsePayload(Object response) {
        if (response instanceof ApiResponse) {
            ApiResponse apiResponse = (ApiResponse) response;
            try {
                String encryptedData = encodeBase64(apiResponse.getPayload().toString());
                apiResponse.setPayload(encryptedData);
            } catch (Exception ex) {
                throw new RuntimeException("Encryption failed", ex);
            }
        }
    }

    private String encodeBase64(String input) throws UnsupportedEncodingException {
        return Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
    }

    private String decodeBase64(String input) throws UnsupportedEncodingException {
        return new String(Base64.getDecoder().decode(input), StandardCharsets.UTF_8);
    }
}

Entity Class

public class Account {
    private Long userId;
    private String username;
    private String email;
    
    // Constructors, getters and setters omitted
}

Controller Layer

@RestController
@RequestMapping("/v1/accounts")
public class AccountController {

    @GetMapping("/search")
    public ApiResponse queryAccount(Account account) {
        System.out.println("Processing request for: " + account.getUsername());
        
        List<Account> results = new ArrayList<>();
        Account sample = new Account();
        sample.setUserId(1001L);
        sample.setUsername("plaintextuser");
        sample.setEmail("demo@example.com");
        results.add(sample);
        
        ApiResponse response = new ApiResponse();
        response.setStatus("SUCCESS");
        response.setPayload(results);
        return response;
    }
}

Response Wrapper

public class ApiResponse {
    private String status;
    private Object payload;
    
    // Constructors, getters and setters omitted
}

Application Entry Point

@SpringBootApplication
public class AopEncryptionApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopEncryptionApplication.class, args);
    }
}

Testing

Start the application and test with a Base64-encoded username parameter:

Request:

http://localhost:8080/v1/accounts/search?username=dGVzdHVzZXI=

Console Output:

Decrypting request parameter: testuser
Processing request for: testuser
Encrypting response payload: [{...}]

Response:

{
  "status": "SUCCESS",
  "payload": "W3sidXNlcklkIjoxMDAxLCJ1c2VybmFtZSI6InBsYWludGV4dHVzZXIiLCJlbWFpbCI6ImRlbW9AZXhhbXBsZS5jb20ifV0="
}

The aspect automatically decrypts incoming parameters and encrypts outgoing responses, demonstrating transparent security handling without polluting business logic.

Tags: spring-boot aop aspectj Encryption Base64

Posted on Tue, 16 Jun 2026 16:38:33 +0000 by robb73