Dans le projet, certaines informations sensibles ne peuvent pas être affichées directement, telles que les numéros de téléphone portable des clients, les cartes d'identité, les numéros de plaque d'immatriculation et d'autres informations. Une désensibilisation des données est requise lors de l'affichage pour éviter les fuites. confidentialité des clients. La désensibilisation consiste à traiter une partie des données avec des symboles de désensibilisation (*).
Lorsque le serveur renvoie des données, utilisez la sérialisation Jackson pour terminer la désensibilisation des données et obtenir un affichage désensibilisé des informations sensibles.
Réduisez le nombre de développements répétés et améliorez l'efficacité du développement
Formez des règles de désensibilisation unifiées et efficaces
Peut être basé sur la méthode de désensibilisation consistant à remplacer la mise en œuvre de la désensibilisation par défaut pour obtenir des services personnalisés évolutifs et personnalisables Exigences de désensibilisation de la scène
StdSerializer : La classe de base utilisée par tous les sérialiseurs standard, elle sert à écrire une sérialisation personnalisée. La classe de base recommandée pour une utilisation par les programmes. .
ContextualSerializer : est une autre interface liée à la sérialisation fournie par Jackson. Sa fonction est de personnaliser JsonSerializer grâce aux informations contextuelles connues par le domaine.
package com.jd.ccmp.ctm.constraints.serializer; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.jd.ccmp.ctm.constraints.Symbol; import com.jd.ccmp.ctm.constraints.annotation.Desensitize; import com.jd.ccmp.ctm.constraints.desensitization.Desensitization; import com.jd.ccmp.ctm.constraints.desensitization.DesensitizationFactory; import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization; import java.io.IOException; /** * 脱敏序列化器 * * @author zhangxiaoxu15 * @date 2022/2/8 11:10 */ public class ObjectDesensitizeSerializer extends StdSerializer<Object> implements ContextualSerializer { private static final long serialVersionUID = -7868746622368564541L; private transient Desensitization<Object> desensitization; protected ObjectDesensitizeSerializer() { super(Object.class); } public Desensitization<Object> getDesensitization() { return desensitization; } public void setDesensitization(Desensitization<Object> desensitization) { this.desensitization = desensitization; } @Override public JsonSerializer<Object> createContextual(SerializerProvider prov, BeanProperty property) { //获取属性注解 Desensitize annotation = property.getAnnotation(Desensitize.class); return createContextual(annotation.desensitization()); } @SuppressWarnings("unchecked") public JsonSerializer<Object> createContextual(Class<? extends Desensitization<?>> clazz) { ObjectDesensitizeSerializer serializer = new ObjectDesensitizeSerializer(); if (clazz != DefaultDesensitization.class) { serializer.setDesensitization((Desensitization<Object>) DesensitizationFactory.getDesensitization(clazz)); } return serializer; } @Override public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { Desensitization<Object> objectDesensitization = getDesensitization(); if (objectDesensitization != null) { try { gen.writeObject(objectDesensitization.desensitize(value)); } catch (Exception e) { gen.writeObject(value); } } else if (value instanceof String) { gen.writeString(Symbol.getSymbol(((String) value).length(), Symbol.STAR)); } else { gen.writeObject(value); }
Remarque : createContextual peut obtenir le type de champ et les annotations. Lorsqu'un champ a une annotation personnalisée, prenez la valeur dans l'annotation et créez une méthode de sérialisation personnalisée, afin que la valeur puisse être obtenue dans la méthode de sérialisation. La méthode createContextual ne sera appelée que la première fois qu'un champ est sérialisé (car les informations contextuelles du champ ne changeront pas pendant l'exécution), il n'y a donc pas lieu de s'inquiéter des problèmes de performances.
3.2.1 Définition de l'interface du désensibilisateur
package com.jd.ccmp.ctm.constraints.desensitization; /** * 脱敏器 * * @author zhangxiaoxu15 * @date 2022/2/8 10:56 */ public interface Desensitization<T> { /** * 脱敏实现 * * @param target 脱敏对象 * @return 脱敏返回结果 */ T desensitize(T target); }
3.2.2 Implémentation de l'usine de désensibilisation
package com.jd.ccmp.ctm.constraints.desensitization; import java.util.HashMap; import java.util.Map; /** * 工厂方法 * * @author zhangxiaoxu15 * @date 2022/2/8 10:58 */ public class DesensitizationFactory { private DesensitizationFactory() { } private static final Map<Class<?>, Desensitization<?>> map = new HashMap<>(); @SuppressWarnings("all") public static Desensitization<?> getDesensitization(Class<?> clazz) { if (clazz.isInterface()) { throw new UnsupportedOperationException("desensitization is interface, what is expected is an implementation class !"); } return map.computeIfAbsent(clazz, key -> { try { return (Desensitization<?>) clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new UnsupportedOperationException(e.getMessage(), e); } });
3.3 .1 Valeur par défaut mise en place de la désensibilisation
peut être étendu pour mettre en œuvre des scénarios personnalisés basés sur l'implémentation par défaut
package com.jd.ccmp.ctm.constraints.desensitization; /** * 默认脱敏实现 * * @author zhangxiaoxu15 * @date 2022/2/8 11:01 */ public interface DefaultDesensitization extends Desensitization<String> { }
3.3.2 Désensibilisateur de numéro de téléphone portable
réalise la désensibilisation des 4 chiffres du milieu du numéro de téléphone mobile
package com.jd.ccmp.ctm.constraints.desensitization; import com.jd.ccmp.ctm.constraints.Symbol; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 手机号脱敏器,保留前3位和后4位 * * @author zhangxiaoxu15 * @date 2022/2/8 11:02 */ public class MobileNoDesensitization implements DefaultDesensitization { /** * 手机号正则 */ private static final Pattern DEFAULT_PATTERN = Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}"); @Override public String desensitize(String target) { Matcher matcher = DEFAULT_PATTERN.matcher(target); while (matcher.find()) { String group = matcher.group(); target = target.replace(group, group.substring(0, 3) + Symbol.getSymbol(4, Symbol.STAR) + group.substring(7, 11)); } return target;
Implémenter des annotations personnalisées via @JacksonAnnotationsInside pour améliorer la facilité d'utilisation
package com.jd.ccmp.ctm.constraints.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.jd.ccmp.ctm.constraints.desensitization.Desensitization; import com.jd.ccmp.ctm.constraints.serializer.ObjectDesensitizeSerializer; import java.lang.annotation.*; /** * 脱敏注解 * * @author zhangxiaoxu15 * @date 2022/2/8 11:09 */ @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = ObjectDesensitizeSerializer.class) @Documented public @interface Desensitize { /** * 对象脱敏器实现 */ @SuppressWarnings("all") Class<? extends Desensitization<?>> desensitization();
3.4.1 Annotation de désensibilisation par défaut
package com.jd.ccmp.ctm.constraints.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization; import java.lang.annotation.*; /** * 默认脱敏注解 * * @author zhangxiaoxu15 * @date 2022/2/8 11:14 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @Desensitize(desensitization = DefaultDesensitization.class) @Documented public @interface DefaultDesensitize {
3.4.2 Annotation de désensibilisation du numéro de téléphone portable
package com.jd.ccmp.ctm.constraints.annotation; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.jd.ccmp.ctm.constraints.desensitization.MobileNoDesensitization; import java.lang.annotation.*; /** * 手机号脱敏 * * @author zhangxiaoxu15 * @date 2022/2/8 11:18 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @Desensitize(desensitization = MobileNoDesensitization.class) @Documented public @interface MobileNoDesensitize { }
Supporté Spécifier des symboles de désensibilisation, tels que * ou ^_^
package com.jd.ccmp.ctm.constraints; import java.util.stream.Collectors; import java.util.stream.IntStream; /** * 脱敏符号 * * @author zhangxiaoxu15 * @date 2022/2/8 10:53 */ public class Symbol { /** * '*'脱敏符 */ public static final String STAR = "*"; private Symbol() {} /** * 获取符号 * * @param number 符号个数 * @param symbol 符号 */ public static String getSymbol(int number, String symbol) { return IntStream.range(0, number).mapToObj(i -> symbol).collect(Collectors.joining()); }
Diagramme des classes de programme
**Analyse du processus d'exécution**
1. Appelez JsonUtil.toJsonString() pour démarrer la sérialisation
2. Identifiez l'annotation @MobileNoDesensitize sur l'attribut mobile (3.4.2 ci-dessus)
3. Appelez ObjectDesensitizeSerializer#createContextual (3.1 & 3.2 ci-dessus) et renvoyez JsonSerializer
4. Appelez la désensibilisation du numéro de téléphone mobile pour implémenter MobileNoDesensitization#desensitize (3.3.2 ci-dessus)
5. Sortez le résultat de la sérialisation désensibilisée, {"mobile": "133****5678"}
Il n'est pas difficile de trouver le noyau Le processus d'exécution est l'étape 3, mais comment sont @MobileNoDesensitize et ObjectDesensitizeSerializer connectés ?
Essayez de trier les liens de référence : @MobileNoDesensitize -> @Desensitize -> @JsonSerialize -> ObjectDesensitizeSerializer
Cependant, dans l'implémentation d'ObjectDesensitizeSerializer, nous ne semblons pas trouver d'appels directs au liens ci-dessus Relation
Il faut dire cela du concept de méta-annotations de Jackson
//**Jackson元注解** //1.提到元注解这个词,大家会想到@Target、@Retention、@Documented、@Inherited //2.Jackson也以同样的思路设计了@JacksonAnnotationsInside /** * Meta-annotation (annotations used on other annotations) * used for indicating that instead of using target annotation * (annotation annotated with this annotation), * Jackson should use meta-annotations it has. * This can be useful in creating "combo-annotations" by having * a container annotation, which needs to be annotated with this * annotation as well as all annotations it 'contains'. * * @since 2.0 */ @Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotation public @interface JacksonAnnotationsInside { }
C'est à travers le mécanisme des "combo-annotations" (annotations combinées, annotations groupées) qui demande à Jackson d'utiliser la méta -annotations qu'il possède, au lieu d'utiliser des annotations cibles, permettant une désensibilisation personnalisée pour atteindre les objectifs de conception.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!