前書き: 実際のプロジェクト開発では、一部のユーザーの個人情報が機密保護されない可能性があります。従来の方法の多くは、replace メソッドを使用して手動で置き換えますが、その結果、多くの冗長なコードが発生し、将来のメンテナンスが容易ではありません。今回は、SpringBoot でのシリアル化を通じてデータの非感作をエレガントに実現する方法を説明します。
目次
4、DesensitizationSerialize 減感シリアライザ
1. pom 依存関係をインポートする
完全なコード:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、DesensitizationEnum 列挙クラス
DesensitizationSerialize シリアル化クラスでは、非感作アノテーションの型値、つまり DesensitizationEnum の型に従って、どの非感作メソッドを使用する必要があるかを決定します。
ここでは単純に 5 つの列挙型を定義します。
完全なコード:
package com.example.desensitization.constant;
public enum DesensitizationEnum {
/**
* 自定义
*/
CUSTOM_RULE,
/**
* 身份证号码
*/
ID_CARD_NO,
/**
* 电话号码
*/
PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 银行卡号
*/
BANK_CARD_NO,
}
3. 減感カスタムアノテーション
これはカスタム アノテーション@Desensitization で、感度を下げる必要があるフィールドをマークするために使用されます。
これには主に次のメタ アノテーションと属性が含まれます。
1. @Target(ElementType.FIELD): このアノテーションがフィールドでのみ使用できることを示します。
2. @Retention(RetentionPolicy.RUNTIME): 注釈が実行時まで保持できることを示します。
3. @JacksonAnnotationsInside: これは Jackson メタアノテーションであり、アノテーションが Json シリアル化アノテーションとして使用できることを示します。
4. @JsonSerialize: アノテーションはシリアル化に DesensitizationSerialize を使用します。
5. DesensitizationEnum 型: 列挙内の感度解除タイプに対応する、感度解除が必要なタイプ。
6. int start/end: オプションの開始位置と終了位置。感度解除タイプが文字列の場合に有効です。
完全なコード:
package com.example.desensitization.annotation;
import com.example.desensitization.constant.DesensitizationEnum;
import com.example.desensitization.serialize.DesensitizationSerialize;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
DesensitizationEnum type();
int start() default 0;
int end() default 0;
}
4、DesensitizationSerialize 減感シリアライザ
1. JsonSerializer<String> の継承 JsonSerializer は、オブジェクトを JSON にシリアル化するコア メソッドを実装する Jackson のシリアライザー基本クラスで、文字列のカスタム シリアル化を実現するために継承されます。
2. ContextualSerializer インターフェイスの実装 ContextualSerializer を使用すると、コンテキストに基づいてシリアライザーをカスタマイズできます。このインターフェイスを実装した後、createContextual() メソッドを実装できます。
キーコード:
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
private DesensitizationEnum type;
private Integer start;
private Integer end;
}
カスタム シリアライザー メソッドは、非侵入的で柔軟な感度の解除、ビジネス コードへの侵入ゼロを実現でき、Spring などのフレームワークに依存しないため、独立したアプリケーション サービスの作成に適しています。もちろん、AOP 実装にも適用可能なシナリオがあり、別の代替手段として使用できます。
createContextual()方法:
1. コントローラーの user() メソッドが呼び出され、User オブジェクトが構築されて返されます。
2. User オブジェクトの JSON シリアル化を開始するには、定義した DesensitizationSerialize の createContextual() メソッドが最初に呼び出されます。このエンティティ クラスが createContextual() メソッドによって処理された場合、このメソッドは今後使用されません。そして、serialize() メソッドが直接使用されます。
3. DesensitizationSerialize は、現在のクラス フィールドに @Desensitization アノテーションがあるかどうかを確認します。N 個ある場合は、アノテーションのタイプ、開始パラメーター、終了パラメーターに従って N 個の DesensitizationSerialize インスタンスを作成します。
キーコード:
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty)
throws JsonMappingException {
if (beanProperty != null) {
// 获取当前正在处理的字段的类型,判断如果是 String 类型则进行后续脱敏逻辑处理
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
// 通过 beanProperty 获取在字段上标注的 @Desensitization 注解
Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
// 如果没有就尝试获取类注解
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
// 不为null
if (desensitization != null) {
// 如果获取到了注解,则根据注解的 type、start 和 end 参数创建 DesensitizationSerialize 实例,这是脱敏处理的序列化器
return new DesensitizationSerialize(desensitization.type(), desensitization.start(),
desensitization.end());
}
}
// 直接返回默认的序列化器
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
// 直接返回默认的序列化器
return serializerProvider.findNullValueSerializer(null);
}
シリアル化メソッド:
JsonGenerator は、Jackjson が提供する JSON ジェネレーター クラスです。カスタム シリアライザーの Serialize() メソッドでは、JsonGenerator インスタンスが渡されます。Serialize() メソッドは、JsonGenerator を通じて、感度を解除された文字列を結果の JSON に書き込む必要があります。
CharSequenceUtil と DesensitizedUtil は両方とも hutool によって提供されるツール クラスです。
全体的なプロセスは次のとおりです。
1. アノテーションのパラメータに従って、感度解除戦略を動的に選択します。
2. Hutool に対応する感度解除関数を呼び出して文字列を処理します。
3. 感度解除の結果を JSON に書き込みます。
キーコード:
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
switch (type){
//自定义
case CUSTOM_RULE:
jsonGenerator.writeString(CharSequenceUtil.hide(s, start, end));
break;
//身份证
case ID_CARD_NO:
jsonGenerator.writeString(DesensitizedUtil.idCardNum(s, 2, 6));
break;
//手机号
case PHONE:
jsonGenerator.writeString(DesensitizedUtil.mobilePhone(s));
break;
//地址
case ADDRESS:
jsonGenerator.writeString(DesensitizedUtil.address(s, 2));
break;
// 银行卡脱敏
case BANK_CARD_NO:
jsonGenerator.writeString(DesensitizedUtil.bankCard(s));
break;
default:
}
}
要約すると、全体的な実行ロジックは次のとおりです。
1. コントローラーはエンティティ オブジェクトを返します。
2. エンティティ オブジェクトが初めて感度を解除されると、createContextual() メソッドが呼び出されます。
3. 現在のエンティティ オブジェクトの Desensitization の注釈が付けられたすべての文字列フィールドを取得し、対応する DesensitizationSerialize インスタンスを作成し、Desensitization Serializer を実現します。
4. Serialize() メソッドでスイッチの処理ロジックを実行し、JsonGenerator が結果の JSON に感度を解除した文字列を書き込みます。
5. Json データを返します。
完全なコード:
package com.example.desensitization.serialize;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.example.desensitization.annotation.Desensitization;
import com.example.desensitization.constant.DesensitizationEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
private DesensitizationEnum type;
private Integer start;
private Integer end;
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
switch (type){
//自定义
case CUSTOM_RULE:
jsonGenerator.writeString(CharSequenceUtil.hide(s, start, end));
break;
//身份证
case ID_CARD_NO:
jsonGenerator.writeString(DesensitizedUtil.idCardNum(s, 2, 6));
break;
//手机号
case PHONE:
jsonGenerator.writeString(DesensitizedUtil.mobilePhone(s));
break;
//地址
case ADDRESS:
jsonGenerator.writeString(DesensitizedUtil.address(s, 2));
break;
// 银行卡脱敏
case BANK_CARD_NO:
jsonGenerator.writeString(DesensitizedUtil.bankCard(s));
break;
default:
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty)
throws JsonMappingException {
if (beanProperty != null) {
// 获取当前正在处理的字段的类型,判断如果是 String 类型则进行后续脱敏逻辑处理
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
// 通过 beanProperty 获取在字段上标注的 @Desensitization 注解
Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
// 如果没有就尝试获取类注解
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
// 不为null
if (desensitization != null) {
// 如果获取到了注解,则根据注解的 type、start 和 end 参数创建 DesensitizationSerialize 实例,这是脱敏处理的序列化器
return new DesensitizationSerialize(desensitization.type(), desensitization.start(),
desensitization.end());
}
}
// 直接返回默认的序列化器
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
// 直接返回默认的序列化器
return serializerProvider.findNullValueSerializer(null);
}
}
5、ユーザーエンティティクラス
感度を解除したいフィールドに @Desensitization(type = DesensitizationEnum. enumeration type) アノテーションを追加するだけです。
完全なコード:
package com.example.desensitization.domain;
import com.example.desensitization.annotation.Desensitization;
import com.example.desensitization.constant.DesensitizationEnum;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
/**
* 主键
*/
private String id;
/**
* 用户名
*/
private String name;
/**
* 身份证号码
*/
@Desensitization(type = DesensitizationEnum.ID_CARD_NO)
private String idCardNo;
/**
* 电话号码
*/
@Desensitization(type = DesensitizationEnum.PHONE)
private String phone;
/**
* 地址
*/
@Desensitization(type = DesensitizationEnum.CUSTOM_RULE,start = 2,end = 5)
private String address;
/**
* 银行卡号
*/
@Desensitization(type = DesensitizationEnum.BANK_CARD_NO)
private String bankCardNo;
}
6. UserController リクエスト層
完全なコード:
package com.example.desensitization.controller;
import com.example.desensitization.domain.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@RequestMapping
public class UserController {
@GetMapping("/user")
public User user(){
User user = User.builder()
.id(UUID.randomUUID().toString())
.name("张三").
idCardNo("32089809285012823").
phone("13919819285").
bankCardNo("62427292012731238812").
address("江苏省南通市").build();
return user;
}
}
7、テストを実行してください
ブラウザ直接アクセス: http://localhost:8080/user
個人情報が遮断されていることがわかります。
8.Giteeのソースコード
ソース コード アドレス: SpringBoot でのプライバシー データの感度解除のエレガントな実装
9. まとめ
上記は、SpringBoot でプライベート データをエレガントに制限解除する方法に関する私の完全なチュートリアルです。ご質問がある場合は、コメント エリアにメッセージを残してください。