Sunday, June 22, 2025
HomeJavaSimple Implementation of GDPR with Side Oriented Programming

Simple Implementation of GDPR with Side Oriented Programming


Key Takeaways

  • GDPR compliance is probably the most under-looked phenomenon, the failure of which is inflicting authorized implications comparable to heavy financial penalties.
  • An software will be compliant to GDPR as early as within the growth stage, making knowledge privateness as a default and necessary function.
  • The software program libraries we use in our day-to-day growth have already got functionalities to make an software GDPR compliant with out having to make use of paid, costly instruments.
  • A quite simple implementation utilizing Spring Boot and AOP (Side Oriented Programming), defined on this article, will make you understand that GDPR compliance isn’t rRocket sScience.
  • Masking, Encryption, Logging are the core of GDPR compliance, they usually should be utilized in all of the artifacts that contain knowledge privateness, comparable to PII (Personally Identifiable Info).

 

GDPR needs to be a default function, added in each single software that handles consumer knowledge, particularly PII (Personally Identifiable Info).

Most organizations take into account GDPR as luxurious and have an impression that it wants particular instruments and specialists to implement it.

After all, data of your complete GDPR specification is required, however as soon as we’re via the foundations, we are able to see that the frameworks and design patterns we already use in our on a regular basis growth can very nicely be used to implement the GDPR guidelines.

Going ahead, all functions needs to be GDPR compliant.

Options We Will Implement

Once we discuss GDPR, the three necessary issues we need to implement are:

  • Encryption – Scrambling the precise knowledge to cover info which wants a particular mechanism to unscramble it.
  • Masking – Hiding a part of the knowledge with a dummy sample to ensure the knowledge is retrieved however the one who reads isn’t authorised to view it totally.
  • Logging – Recording each motion that entails PII.

Despite the fact that these are three totally different options, the implementation will be simplified and concentrated by utilizing easy programming patterns.

Methods We Will Use

  • Spring Boot framework – For the standard internet software
  • Java customized annotation method – To annotate strategies that wants GDPR remedy
  • Side Oriented Programming (AOP) – Interception-based processing
  • Easy Encryption and Masking Class  – A mock Java class that scrambles, unscrambles and masks the information.
  • Jackson Serializer – To course of Java objects.

Excellent news. We don’t want any third-party libraries for this implementation.

Overview

[Click on the image to view full-size]

Spring Boot Software

Allow us to assume a easy use case:

  • An API that accepts a Consumer Object and returns a response.
  • A service that saves the information to the database and returns saved knowledge again to the Controller.

Be aware: We aren’t going to have a working database connection for this instance, we are going to simply assume that the service saves the information and returns the identical to the consumer.

Entity


public class Consumer {

    non-public String identify;

    public Consumer(){}

    public String getName() {
        return identify;
    }

    public void setName(String identify) {
        this.identify = identify;
    }

    @Override
    public String toString() {
        return "Consumer{" +
                "identify="" + identify + "'' +
                '}';
    }
}

Service


@Service
public class UserService {

    non-public Logger logger = LoggerFactory.getLogger( "UserService" );

    public Consumer create( Consumer consumer ){
        logger.information( " Information inside create service " + consumer );
        return consumer;
    }
}

Controller


@RestController
public class UserController {

    non-public Logger logger = LoggerFactory.getLogger( "UserController" );

    @Autowired UserService userService;

    @PostMapping("/plain")
    public Consumer plain(@RequestBody  Consumer consumer){
        logger.information( " Information within the plain controller "+ consumer);
        return userService.create(consumer);
    }
}


Essential Software (For the sake of completion)


@SpringBootApplication
public class Software {
    public static void predominant(String[] args) {
        SpringApplication.run( Software.class );
    }
}

Customized Annotation

The second step is the customized annotation. Allow us to use commonplace Java to create it.


@Goal( ElementType.METHOD)
@Retention( RetentionPolicy.RUNTIME)
public @interface GDPR { }

Allow us to break down this tradition annotation:

  • The @interface (as an alternative of simply interface)  – makes this class accessible for use as @GDPR in different courses.
  • @Goal(ElementType.METHOD) – makes this annotation technique particular (i.e., can be utilized solely on strategies not on courses or fields).
  • @Retention(RetentionPolicy.RUNTIME) – instructs the compiler to course of this throughout runtime. The annotation needn’t be processed throughout compile time, so we use the RUNTIME retention coverage.

Now this interface can be utilized as @GDPR in any desired technique.

AOP Implementation

Spring Boot natively helps AOP. Utilizing which, we are going to intercept any technique that’s annotated with the @GDPR annotation.

Allow us to go forward and create an interceptor class.


@Side
@Element
public class GDPRInterceptor {

    non-public Logger logger = LoggerFactory.getLogger( GDPRInterceptor.class );

    @Round("@annotation(GDPR)")
    public Object encrypt(ProceedingJoinPoint joinPoint ) throws Throwable {
        logger.information( "Technique intercepted " );
}
  • @Element – This Class is a typical Spring part.
  • @Side  – This Class is a Spring AOP part and may intercept calls primarily based on the sample offered.
  • @Round (“@annotation(GDPR)“) –  This line has two elements:
    • a)    The @Round helps to set off these interceptors each earlier than execution (when the tactic known as) and after execution (after processing is full and earlier than returning the ultimate worth).
    • b)    The parameter (“@annotation (GDPR)“) specifies which strategies are intercepted. In our case, any technique with the annotation @GDPR, is intercepted.

Controller with Annotation

Allow us to add a brand new technique to our controller with the @GDPR annotation. The modified controller ought to appear like beneath:


@RestController
public class UserController {

    non-public Logger logger = LoggerFactory.getLogger( "UserController" );

    @Autowired UserService userService;

    @PostMapping("/plain")
    public Consumer plain(@RequestBody  Consumer consumer){
        logger.information( " Information within the plain controller "+ consumer);
        return userService.create(consumer);
    }

    @PostMapping("/encrypt")
    @GDPR
    public Consumer encrypt(@RequestBody  Consumer consumer){
        logger.information( " Information within the encryption controller "+ consumer);
        return userService.create(consumer);
    }

}


Allow us to break down this controller:

The Controller/Internet API has two strategies with corresponding endpoints, /plain and /encrypt.

  • Each APIs have the identical return sort and parameter sort.
  • Each the strategies are calling the identical service strategies with the identical knowledge
  • Solely the encrypt technique has the @GDPR annotation.

This fashion, the GDPR implementation doesn’t disturb the present enterprise logic.

In case you run the appliance as such and name the /plain API, it’s best to see log statements solely from the UserController and UserService courses. However once you name the /encrypt API, it’s best to see a further log assertion from the GDPRInterceptor class. It needs to be famous that the log assertion from GDPRInterceptor is printed first, as a result of the controller is tapped earlier than it calls the service, which could be very essential on this implementation.

Encryption Service

Let’s create an EncryptionService class. We aren’t going to implement an precise working encryption for now, as an alternative, allow us to take into account a easy mockup of an encryption.


@Element
public class EncryptionService {

    non-public Object encrypt( Object knowledge ){
        
        if( knowledge != null) {
            knowledge += " { ENCRYPTED } ";
        }
        return knowledge;
    }

    non-public Object decrypt( Object knowledge ){
        
        String plainText = null;
        if( knowledge != null ) {
            plainText = knowledge.toString().change(" { ENCRYPTED } ", "");
        }
        return plainText;
    }
}

We’ve two strategies:

  • encrypt – Receives a single object, appends a string referred to as {ENCRYPTED} and returns the identical.
  • decrypt – Receives a single object and removes the string {ENCRYPTED} from it.

For Instance, if the information is “MySampleData,” the encrypt technique will return “MySampleData { ENCRYPTED },”  and in case you cross this output to the decrypt technique, you’ll get again the unique knowledge.

Customizing Object Mapper

The encrypt and decrypt strategies we created are good for processing primitives. However a typical software would solely cope with Java objects. It could be tedious to create particular person transformers for each class sort that an software offers with. For that objective, we’re going to prolong the ObjectMapper class offered by Jackson Library. We’re going to alter the default object serialization technique to incorporate our encryption course of.

First, we have to create two Customized Serializers, one for encryption and one for decryption.

Encryption Serializer


public class EncryptionSerializer extends JsonSerializer<Map> {

    non-public Logger logger = LoggerFactory.getLogger( EncryptionSerializer.class );

    EncryptionService encryptionService = new EncryptionService();

    public EncryptionSerializer() {
        tremendous();
    }

    @Override
    public void serialize(Map t, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

        jsonGenerator.writeStartObject();

        t.forEach((ok,v)->{
            strive {
                jsonGenerator.writeObjectField(  ok.toString(),encryptionService.encrypt( v ));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        jsonGenerator.writeEndObject();
    }
}

Decryption Serializer


public class DecryptionSerializer extends JsonSerializer<Map> {

    EncryptionService encryptionService = new EncryptionService();

    public DecryptionSerializer() {
        tremendous();
    }

    @Override
    public void serialize(Map t, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {

        jsonGenerator.writeStartObject();
        t.forEach((ok,v)->{
            strive {
                jsonGenerator.writeObjectField(  ok.toString(),encryptionService.decrypt( v ));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        jsonGenerator.writeEndObject();
    }
}

The customized serializers prolong JSONSerializer and override the serialize technique. We’re additionally casting this operate with the Map class, which shall be defined later on this article.

Configuring Object Mappers

Allow us to construct the customized object mappers that use these new serializers, because of Spring Boot configuration.


@Configuration
public class ApplicatonConfiguration {

    @Bean
    @Main
    public ObjectMapper objectMapper(){
        return new ObjectMapper();
    }

    @Bean("encryptor")
    public ObjectMapper encryptionMapper(){

        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer( Map.class, new EncryptionSerializer());
        mapper.registerModule( module );

        return mapper;
    }

    @Bean("decryptor")
    public ObjectMapper decryptionMapper(){

        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer( Map.class, new DecryptionSerializer());
        mapper.registerModule( module );

        return mapper;
    }
}

Right here now we have three forms of ObjectMappers:

@Main – the default ObjectMapper utilized by Spring.
@Bean("encryptor")ObjectMapper, that can encrypt the Java Object (JSON).
@Bean("decryptor")ObjectMapper, that can decrypt the Java Object (JSON).

Generalized the Jackson Mapper with java.util.Map

A typical software could have many forms of Java courses/entities. Since we’re going to reconfigure the Jackson serializer offered by Spring itself, we won’t be able to incorporate a Generic <?> to our Serializer. It’s going to even be tiresome to create a serializer for each class sort. So we’re going to generalize all Java class varieties as a Key Worth object i.e., java.util.Map. This generalization will assist us serialize and deserialize any object, with out worrying about precise knowledge sort.

Interceptor with Encryption

Think about the next situation:

  • The controller receives plain textual content knowledge from the consumer via an API name.
  • The controller calls the service technique to avoid wasting the consumer knowledge.
  • The service saves the information in encrypted format and returns the information again to the controller.
  • The controller receives encrypted knowledge from the service, however returns plain textual content again to the consumer as an API response.

To fulfill the above situation, we’d like the information to be encrypted from the controller to the service and decrypted from the service to the controller.

Allow us to add this transformation code to our interceptor.


@Side
@Element
public class GDPRInterceptor {

    non-public Logger logger = LoggerFactory.getLogger( GDPRInterceptor.class );

    @Autowired ObjectMapper mapper;

    @Autowired @Qualifier("encryptor") ObjectMapper encryptor;

    @Autowired @Qualifier("decryptor") ObjectMapper decryptor;

    @Round("@annotation(GDPR)")
    public Object intercept(ProceedingJoinPoint joinPoint ) throws Throwable {
        logger.information( " Technique execution begins" );

        Object[]  params = joinPoint.getArgs();
        Class<?> aClass = null;
        Map<String,Object> intermediateData = null;

        for( int index = 0 ; index < params.size ; index++ ){
            aClass = params[index].getClass();
            intermediateData = mapper.convertValue( params[index], new TypeReference<Map<String, Object>>() {}
 );
            params[index] = encryptor.convertValue(intermediateData, aClass);
        }

        Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();

        Object response = joinPoint.proceed(params);

        intermediateData = mapper.convertValue(response, new TypeReference<Map<String, Object>>() {});
        response = decryptor.convertValue( intermediateData, returnType );
        logger.information ( " Technique execution full" );

        return response;
    }
}

The up to date GDPRInterceptor has the next adjustments:

  • The reconfigured Object Mappers are included with the correct @Qualifier.
  • The tactic parameters are iterated, and each parameter is processed.
  • The Parameter worth is flattened utilizing the usual Object Mapper to a Map Object.
  • The Map Object is then encrypted utilizing the encryptor variant of ObjectMapper and sort casted to the precise parameter knowledge sort.
  • The Encrypted parameter is now forwarded to the Service.
  • The Service saves the encrypted knowledge and returns the identical.
  • The returned encrypted knowledge is as soon as once more flattened to Map utilizing the Commonplace Object Mapper.
  • The flattened Map object is now decrypted utilizing the decryptor variant of ObjectMapper and sort casted to the unique return sort.

Fast Testing

Plain Textual content API

Name



curl --location --request POST 'localhost:8080/plain' --header 'Content material-Sort: software/json' --data-raw '{"identify”: "Developer"}'



Response



{"identify": "Developer"}

Log Statements



INFO 9884 --- [nio-8080-exec-4] UserController                           :  Information within the plain controller Consumer{identify="Developer"}
INFO 9884 --- [nio-8080-exec-4] UserService                              :  Information inside create service Consumer{identify="Developer"}



Encrypted Textual content API

Name



curl --location --request POST 'localhost:8080/encrypt' --header 'Content material-Sort: software/json' --data-raw '{"identify”: "Developer"}'



Response


{"identify": "Developer"}


Log Statements


INFO 9068 --- [nio-8080-exec-2] GDPRInterceptor                          :  Technique execution begins
INFO 9068 --- [nio-8080-exec-2] UserController                           :  Information within the encryption controller Consumer{identify="Developer { ENCRYPTED } "}
INFO 9068 --- [nio-8080-exec-2] UserService                              :  Information inside create service Consumer{identify="Developer { ENCRYPTED } "}
INFO 9068 --- [nio-8080-exec-2] GDPRInterceptor                          :  Technique execution full



As you’ll be able to see, the API response is identical (unique knowledge) for each APIs. Nevertheless, for the encrypt API, the log statements from controller and repair present encrypted knowledge.

Each controller strategies are doing the identical activity, however simply the @GDPR implementation is doing the magic for us.

Masking

Masking delicate knowledge is a crucial side of GDPR compliance. Fortuitously,  it’s straightforward to incorporate it in our present sample.

Allow us to re-assume the situation we mentioned above.

The encryption stays the identical, however when the API responds again to the consumer, the unique knowledge is masked. So, to fulfill this situation, we have to introduce a brand new technique in EncryptionService for Masking and modify the decrypt technique to name this new masking operate.


public Object masks( Object knowledge ){
    String maskedText = null;
    String dataAsString = knowledge.toString();
    if( knowledge != null )
        maskedText = dataAsString.change( dataAsString.substring( 1, 3 ), "XXX");
    return maskedText;
}

public Object decrypt( Object knowledge ){

    String plainText = null;
    if( knowledge != null ) {
        plainText = knowledge.toString().change(" { ENCRYPTED } ", "");
    }
    return masks(plainText);
}


Now in case you name the /encrypt API, the response shall be:


{"identify": "DXXXeloper"}


Logging

We already added logging to the interceptor. Any technique that’s annotated with @GDPR are delicate strategies. At any time when these strategies are referred to as, the interceptor will log it across the execution.

Be aware – It’s endorsed to not log the precise knowledge. Log recordsdata containing PII knowledge are thought-about as a safety threat.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments