preface

What is data desensitization

Data desensitization refers to deforming some sensitive information through desensitization rules to achieve reliable protection of sensitive privacy data

Commonly used desensitization rules

Replace, rearrange, encrypt, truncate, mask

Good data desensitization implementation

1, as far as possible for desensitization after the application, retain meaningful information before desensitization 2, to prevent hackers from cracking to the greatest extent

Today we talk about how to customize data desensitization

The overall train of thought

This example implements desensitization through substitution and then quickly implements desensitization with common framework features such as mybatis interceptor mechanism or JSON serialization

The concrete floor

1. Define a desensitization tool class

You can reference the Hutool toolkit directly, but it does not provide the tool until version 5.6+ www.hutool.cn/docs/#/core…

Otherwise, implement one yourself, which looks like this

public class DesensitizedUtils {



	/** * desensitization, Elimination strategy using the default * < pre > * DesensitizedUtil desensitized (" 100 ", DesensitizedUtils. DesensitizedType. USER_ID)) = "0" * DesensitizedUtil desensitized (" Duan Zhengchun ", DesensitizedUtils. DesensitizedType. CHINESE_NAME)) = "period of * * * DesensitizedUtil. Desensitized (" 51343620000320711 x", DesensitizedUtils.DesensitizedType.ID_CARD)) = "5***************1X" * DesensitizedUtil.desensitized("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)) = "0915*****79" * DesensitizedUtil.desensitized("18049531999", DesensitizedUtils. DesensitizedType. MOBILE_PHONE)) = "180 1999" * * * * * DesensitizedUtil desensitized (" haidian district horse even depression street no. 289 ", DesensitizedUtils. DesensitizedType. ADDRESS)) = "haidian district ma * * * * * * * *" * DesensitizedUtil.desensitized("[email protected]", DesensitizedUtils.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn" * DesensitizedUtil.desensitized("1234567890", DesensitizedUtils. DesensitizedType. PASSWORD)) = "* * * * * * * * * *" * DesensitizedUtil desensitized (" Sue D40000 ", DesensitizedUtils. DesensitizedType. CAR_LICENSE)) = D4 su * * * "0" * DesensitizedUtil desensitized (" 11011111222233333256 ", DesensitizedUtils.DesensitizedType.BANK_CARD)) = "1101 **** **** **** 3256" * </pre> * *@paramSTR string *@paramDesensitizedType Desensitization type; Can desensitization: user ID, Chinese name, ID number, landline number, mobile phone number, address, email, password *@returnDesensitization after the string *@author dazer and neusoft and qiaomu
	 * @since5.6.2 * /
	public static String desensitized(CharSequence str, DesensitizedType desensitizedType) {
		if (StrUtil.isBlank(str)) {
			return StrUtil.EMPTY;
		}
		String newStr = String.valueOf(str);
		switch (desensitizedType) {
			case USER_ID:
				newStr = String.valueOf(DesensitizedUtils.userId());
				break;
			case CHINESE_NAME:
				newStr = DesensitizedUtils.chineseName(String.valueOf(str));
				break;
			case ID_CARD:
				newStr = DesensitizedUtils.idCardNum(String.valueOf(str), 1.2);
				break;
			case FIXED_PHONE:
				newStr = DesensitizedUtils.fixedPhone(String.valueOf(str));
				break;
			case MOBILE_PHONE:
				newStr = DesensitizedUtils.mobilePhone(String.valueOf(str));
				break;
			case ADDRESS:
				newStr = DesensitizedUtils.address(String.valueOf(str), 8);
				break;
			case EMAIL:
				newStr = DesensitizedUtils.email(String.valueOf(str));
				break;
			case PASSWORD:
				newStr = DesensitizedUtils.password(String.valueOf(str));
				break;
			case CAR_LICENSE:
				newStr = DesensitizedUtils.carLicense(String.valueOf(str));
				break;
			case BANK_CARD:
				newStr = DesensitizedUtils.bankCard(String.valueOf(str));
				break;
			default:}return newStr;
	}

	/** * [userId] userId ** is not provided externally@returnPrimary key */ after desensitization
	public static Long userId(a) {
		return 0L;
	}

	/** * [Chinese name] only show the first character, other hidden 2 asterisks, such as: li ** **@paramFullName name *@returnDesensitized name */
	public static String chineseName(String fullName) {
		if (StrUtil.isBlank(fullName)) {
			return StrUtil.EMPTY;
		}
		return StrUtil.hide(fullName, 1, fullName.length());
	}

	/** ** the first and last two digits **@paramIdCardNum Id card *@paramFront reserved: the front digit; Starting at 1 *@paramEnd reserved: the following end digit; Starting at 1 *@returnId card after desensitization */
	public static String idCardNum(String idCardNum, int front, int end) {
		// The id card cannot be empty
		if (StrUtil.isBlank(idCardNum)) {
			return StrUtil.EMPTY;
		}
		// The length must not be longer than the id number
		if ((front + end) > idCardNum.length()) {
			return StrUtil.EMPTY;
		}
		// Truncate must not be less than 0
		if (front < 0 || end < 0) {
			return StrUtil.EMPTY;
		}
		return StrUtil.hide(idCardNum, front, idCardNum.length() - end);
	}

	/** ** ** ** *@paramNum Fixed number *@returnFixed telephone after desensitization; * /
	public static String fixedPhone(String num) {
		if (StrUtil.isBlank(num)) {
			return StrUtil.EMPTY;
		}
		return StrUtil.hide(num, 4, num.length() - 2);
	}

	/** * [mobile phone number] the first three digits, the last four digits, other hidden, such as 135****2210 **@paramNum Mobile phone; *@returnMobile phone after desensitization; * /
	public static String mobilePhone(String num) {
		if (StrUtil.isBlank(num)) {
			return StrUtil.EMPTY;
		}
		return StrUtil.hide(num, 3, num.length() - 4);
	}

	/** * [address] display only to region, do not display the detailed address, such as: haidian district, Beijing **** **@paramHome address *@paramSensitiveSize Length of sensitive information *@returnHome address after desensitization */
	public static String address(String address, int sensitiveSize) {
		if (StrUtil.isBlank(address)) {
			return StrUtil.EMPTY;
		}
		int length = address.length();
		return StrUtil.hide(address, length - sensitiveSize, length);
	}

	/** * [email] Only the first letter of the prefix is displayed, the prefix is hidden, replaced by an asterisk, @ and the following address is displayed, such as: d**@126.com **@paramEmail mailbox *@returnEmail */ after desensitization
	public static String email(String email) {
		if (StrUtil.isBlank(email)) {
			return StrUtil.EMPTY;
		}
		int index = StrUtil.indexOf(email, The '@');
		if (index <= 1) {
			return email;
		}
		return StrUtil.hide(email, 1, index);
	}

	/** * [password] Replace all characters of the password with *, for example, ****** **@param"Password," password *@returnDesensitized password */
	public static String password(String password) {
		if (StrUtil.isBlank(password)) {
			return StrUtil.EMPTY;
		}
		return StrUtil.repeat(The '*', password.length());
	}

	Eg1: null - "" * EG1: "" - "" * eg3: sud40000 - "sud4 ***0 * eg4: shaanxi A12345D -" shaanxi A1****D * eg5: Jinga123 - jingA123 will not be processed if the license plate is wrong@paramCarLicense The complete license plate number *@returnDesensitized license plate */
	public static String carLicense(String carLicense) {
		if (StrUtil.isBlank(carLicense)) {
			return StrUtil.EMPTY;
		}
		// Common license plate
		if (carLicense.length() == 7) {
			carLicense = StrUtil.hide(carLicense, 3.6);
		} else if (carLicense.length() == 8) {
			// New energy license plate
			carLicense = StrUtil.hide(carLicense, 3.7);
		}
		return carLicense;
	}

	/** * Bank card number desensitization * eg: 1101 **** **** **** 3256 **@paramBankCardNo Bank card number *@returnDesensitization after the bank card number *@since5.6.3 * /
	public static String bankCard(String bankCardNo) {
		if (StrUtil.isBlank(bankCardNo)) {
			return bankCardNo;
		}
		bankCardNo = StrUtil.trim(bankCardNo);
		if (bankCardNo.length() < 9) {
			return bankCardNo;
		}

		final int length = bankCardNo.length();
		final int midLength = length - 8;
		final StringBuilder buf = new StringBuilder();

		buf.append(bankCardNo, 0.4);
		for (int i = 0; i < midLength; ++i) {
			if (i % 4= =0) {
				buf.append(CharUtil.SPACE);
			}
			buf.append(The '*');
		}
		buf.append(CharUtil.SPACE).append(bankCardNo, length - 4, length);
		returnbuf.toString(); }}Copy the code

In fact, normal to this step, through the replacement of desensitization can be completed, you can directly in the program, directly call the tool on the line. But as a lazy programmer, this is not enough. So we’re going to encapsulate it further

2, custom desensitization notes

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sensitive {

    DesensitizedType strategy(a) default DesensitizedType.NONE;

    /** * Whether to use the DFA algorithm *@return* /
    boolean useDFA(a) default false;

    /** * DFA sensitive character replacement, default to "*" *@return* /
    String dfaReplaceChar(a) default "*";


    /** * Number of dFA sensitive character replacements *@return* /
    int dfaReplaceCharRepeatCount(a) default 1;

}

Copy the code

3. Take advantage of some framework features to improve efficiency

A. If the project already uses MyBATIS, you can take advantage of the MyBATIS interceptor feature. The implementation principle is to intercept the response back results, and then desensitization of the results

@Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = Statement.class))
public class DesensitizedInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        List<Object> list = (List<Object>) invocation.proceed();
        list.forEach(EntityUtils::desensitized);

        returnlist; }}Copy the code

B. If the project is a Web project based on SpringBoot, you can use springboot’s own Jackson custom serialization implementation. The original implementation is desensitization of JSON when it is serialized and rendered to the front-end.

If this is the case, the custom annotations need to be modified, plus

@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizedJsonSerializer.class)
Copy the code

Annotation. Form the following

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizedJsonSerializer.class)
public @interface Sensitive {

    DesensitizedType strategy(a) default DesensitizedType.NONE;

    /** * Whether to use the DFA algorithm *@return* /
    boolean useDFA(a) default false;

    /** * DFA sensitive character replacement, default to "*" *@return* /
    String dfaReplaceChar(a) default "*";


    /** * Number of dFA sensitive character replacements *@return* /
    int dfaReplaceCharRepeatCount(a) default 1;

}

Copy the code

Serialized desensitization logic core code is as follows

public class DesensitizedJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private Sensitive sensitive;
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(EntityUtils.getDesensitizedValue(sensitive,s));

    }

    @Override
    publicJsonSerializer<? > createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty)throws JsonMappingException {

        sensitive = beanProperty.getAnnotation(Sensitive.class);

        if(! ObjectUtils.isEmpty(sensitive) && String.class.isAssignableFrom(beanProperty.getType().getRawClass())){return this;
        }
        returnserializerProvider.findValueSerializer(beanProperty.getType(),beanProperty); }}Copy the code

The sample

Take json as an example

1. Define entity objects and add desensitization annotations to desensitization attributes

@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserDTO {

    private Integer id;

    private String username;

    @Sensitive(strategy = DesensitizedType.PASSWORD)
    private String password;

    @Sensitive(strategy = DesensitizedType.CHINESE_NAME)
    private String fullname;

    @Sensitive(strategy = DesensitizedType.MOBILE_PHONE)
    private String mobile;

    @Sensitive(strategy = DesensitizedType.EMAIL)
    private String email;

    @Sensitive(useDFA = true,dfaReplaceChar = "#",dfaReplaceCharRepeatCount = 3)
    private String remark;
}

Copy the code

2. Write a test Controller

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;


    @GetMapping(value="/list")
    public AjaxResult listUsers(a){
       returnAjaxResult.success(userService.listUserDTO()); }}Copy the code

The test results

Desensitization has taken place as shown

Other options

1. Data desensitization based on Sharding Sphere

Specific implementation can refer to the following article

Jaskey. Making. IO/blog / 2020/0…

2. Custom annotation formatting

The main implementation steps are as follows

  • 1, implement AnnotationFormatterFactory interface

  • Create a desensitized Formatter class to implement Formatter

  • 3, will be registered to FormatterRegistry AnnotationFormatterFactory implementation interface

Specific implementation can refer to the following article

Blog.csdn.net/qq_27081015…

4. Desensitization with FastJSON

The main implementation steps are as follows

  • 1. Implement ValueFilter interface and desensitize in Process

  • 2. Configure FastJSON as the default JSON conversion

/** * Configure fastjson as the default JSON conversion **@return* /
    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters(a) {
        // 1. Define an object that converters messages
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 2. Add fastJSON configuration information, such as whether to format the returned JSON data
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter());// Add your own interceptor
        // 3. Add configuration information to Converter
        fastConverter.setFastJsonConfig(fastJsonConfig);
        // 4. Assign the Converter to HttpMessageConverterHttpMessageConverter<? > converter = fastConverter;// 5. Return the HttpMessageConverters object
        return new HttpMessageConverters(converter);
    }

Copy the code

Specific implementation can refer to the following article

Blog.csdn.net/qq_27081015…

5, use mybatis-mate

Mybatis -plus enterprise (data elegant processing) module, use to configure the authorization code. The following

mybatis-mate:
  cert:
    grant: jinTianYiXueKe
    license: GKXP9r4MCJhGID/DTGigcBcLmZjb1YZGjE4GXaAoxbtGsPC20sxpEtiUr2F7Nb1ANTUekvF6Syo6DzraA4M4oacwoLVTglzfvaEyUogW8L7mydqlsZ4+hlm2 0kK85eLJK1QsskrSJmreMnEaNh9lsV7Lpbxy9JeGCeM0HPEbRvq8Y+8dUt5bQYLklsa3ZIBexir+4XykZY15uqn1pYIp4pEK0+aINTa57xjJNoWuBIqm7BdF Ib4l1TAcPYMTsMXhF5hfMmKD2h391HxWTshJ6jbt4YqdKD167AgeoM+B+DE1jxlLjcpskY+kFs9piOS7RCcmKBBUOgX2BD/JxhR2gQ==
Copy the code

Its implementation mechanism is json serialization, if you are interested in the link below

Gitee.com/baomidou/my…

Mybatis -mate – based desensitization of demo, link to github.com/lyb-geek/sp…

conclusion

Sometimes there are various ways to implement business scenarios, so we should know how to make a choice. For example, if your project does not use MyBatis, but to introduce MyBatis for desensitization, this scheme will add extra complexity, and it will be difficult to maintain later

The demo link

Github.com/lyb-geek/sp…