How to mask a Field or an Attribute in Java

Here I am going to show you how to mask a field or an attribute in Java programming language. This kind of situation occurs when you are dealing with mainly debit or credit cards. So you have a POJO or model class for your card (debit or credit) but for security reason you do not want to display the card number, then you need to mask the card number.

Masking something means you want to replace the actual value with some other value, for example, your card number is 3456 7890 2345 and you want to replace with X or *. So the masking value would be XXXX XXXX XXXX or **** **** ****. You may also want to show or display only last 4 digits instead of showing all digits or hiding all digits.

In this example I am going to show you how to replace all numbers by a particular value or show only last 4 digits of the cards.

I am going to create custom annotation with default value and the target of this annotation will be field type and retention policy will be runtime. I will create two annotations – one will be used to replace all values at once and another one will be used to replace each character one by one when you want to mask some digits and show rest of the digits.

To mask each character or digit one by one in a string, I have created the following annotation.

package com.roytuts.java.mask.field.externalizable;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface MaskChar {

    char value() default 'X';

}

The default value is X and if you do not specify any value for this annotation when applied on field, the default value will be used. For example, when you write something like the following:

@MaskChar
private String identifier;

If you want to change the default value, for example, instead of showing X, show *, then you can do something as given below:

@MaskChar
('*')
private String identifier;

The @MaskChar annotation is mainly used for masking single character.

Next I am going to show another custom annotation that will replace all digits or characters at once.

package com.roytuts.java.mask.field.externalizable;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface MaskString {

    String value() default "XXXX";

}

The above annotation will replace entire input string with the annotated value. In this case also you can either use the default value or you can put your own value using @MaskString annotation.

Let’s say you have the following Card class that has the field or attribute identifier that holds the card number. This card may be either debit or credit card and you do not want to display or show card number. So I have applied the @MaskString annotation to replace with the default value. You can also replace with the * as shown in the line that is commented out.

public class Card {

    private Type type;
    private String name;

    @MaskString
    //@MaskString("****")
    private String identifier;
}

The actual masking happens on the identifier field using the following code snippets. This code snippets check whether the required annotation is present or not and accordingly apply the mask value to the field.

if (field.isAnnotationPresent(MaskString.class)) {
	MaskString mask = field.getAnnotation(MaskString.class);
	if (mask.value() != null && !mask.value().isEmpty()) {
		return mask.value();
	}
}

Next coming to the char masking, here I check also whether the required annotation is present or not and according replace each character or digit by the mask char except the last 4 digits or characters.

if (field.isAnnotationPresent(MaskChar.class)) {
	MaskChar mask = field.getAnnotation(MaskChar.class);
	if (mask.value() != ' ') {
		String part1 = currValue.substring(0, currValue.length() - 4).replaceAll("[0-9]",
				String.valueOf(mask.value()));

		String part2 = currValue.substring(currValue.length() - 4);

		return part1 + part2;
	}
}

The examples I have shown above to mask the field or attribute using custom annotation can also be achieved using direct value replacements.

Testing the Masking

Now let’s test the implementation. Create a class with main method to test sample data.

package com.roytuts.java.mask.field.externalizable;

import java.io.IOException;

public class MaskTesting {

    public static void main(String[] args) throws IOException, IOException, ClassNotFoundException {
        Card debitCard = new Card();
        debitCard.setType(Type.debit);
        debitCard.setName("Platinum");
        debitCard.setIdentifier("4567 8902 4365");

        System.out.println("Debit Card: " + debitCard);

        Card creditCard = new Card();
        creditCard.setType(Type.credit);
        creditCard.setName("Gold");
        creditCard.setIdentifier("5562 9354 6232");

        System.out.println("Credit Card: " + creditCard);
    }

}

For string masking, let’s say the identifier field is annotated as follows:

@MaskString
private String identifier;

Running main class will give you the following output:

Debit Card: Card [type=debit, name=Platinum, identifier=XXXX]
Credit Card: Card [type=credit, name=Gold, identifier=XXXX]

For character masking, let’s say the identifier field is annotated as follows:

@MaskChar
private String identifier;

Running the above main class will give you the following output:

Debit Card: Card [type=debit, name=Platinum, identifier=XXXX XXXX 4365]
Credit Card: Card [type=credit, name=Gold, identifier=XXXX XXXX 6232]

If you do not apply any annotation from the above then your card number will not be masked.

Debit Card: Card [type=debit, name=Platinum, identifier=4567 8902 4365]
Credit Card: Card [type=credit, name=Gold, identifier=5562 9354 6232]

Source Code

Download

Leave a Reply

Your email address will not be published.