WEB开发之敏感数据处理(一) - JPA敏感数据自动加解密

news/2024/11/28 17:49:00/

实现原理

JPA提供AttributeConverter接口用于实现数据库和实体之间数据的转换。利用这个特性可以在转换时进行加解密,从而实现自动加解密的功能

定义一个 Converter

  • 定义一个SensitiveConverter 实现JPA的 AttributeConverter<String, String>
  • convertToDatabaseColumn entity值转换为数据库值,可实现加密逻辑
  • convertToEntityAttribute 将数据库值转换为entity值,可实现解密
@Converter
public class SensitiveConverter implements AttributeConverter<String, String> {@Overridepublic String convertToDatabaseColumn(String attribute) {// TODO: 2023/5/30 加密逻辑 return null;}@Overridepublic String convertToEntityAttribute(String dbData) {// TODO: 2023/5/30 解密逻辑 return null;}
}

定义一个实体

定义一个User实体,通过@Convert(converter = SensitiveConverter.class)注解的需要加密的属性。

  • 在JPA入库时会自动调用SensitiveConverter#convertToDatabaseColumn 方法,在这个方法内实现加密逻辑
  • 在JPA查询时会自动调用SensitiveConverter#convertToEntityAttribute方法,可以实现解密逻辑
@Getter
@Setter
@Entity
public class User {@Idprivate Long id;private String username;@Convert(converter = SensitiveConverter.class)private String password;
}

优雅实现

上边的是实现方式,需要在字段上增加@Convert(converter = SensitiveConverter.class),由于@Convert可用用于任意类型的转换,converter的值可以是任意实现了AttributeConverter的类型。
这里我们可以借助JSR 269: Pluggable Annotation Processing API 通过插件式注解处理方式,通过定义一个Sensitive注解,在编译时自动替换成@Convert(converter = SensitiveConverter.class) (有点类似于C的宏)

定义敏感数据注解 - @Sensitive

  • @Retention(RetentionPolicy.SOURCE):注解仅在源码阶段生效,编译后会自动转换为@Convert(converter = SensitiveConverter.class)
  • @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}): 可使用注解的范围,这里和@Convert保持一致即可
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface Sensitive {
}

定义注解处理器 - SensitiveProcessor

继承AbstractProcessor 在编译时对注解进行处理

@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
@SupportedAnnotationTypes(value = "net.reduck.sdp.annotation.Sensitive")
public class SensitiveProcessor extends AbstractProcessor {private Types types = null;private Elements elements = null;private Filer filer = null;private Messager messager = null;private JavacTrees trees;private TreeMaker treeMaker;private Names names;private Symtab symtab;private AstMojo astMojo;public SensitiveProcessor() {}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {// 判断方法应该添加什么注解for (Element element : roundEnvironment.getElementsAnnotatedWith(Sensitive.class)) {javax.lang.model.element.Name methodName = element.getSimpleName();TypeElement typeElem = (TypeElement) element.getEnclosingElement();String typeName = typeElem.getQualifiedName().toString();astMojo.importIfAbsent(element, Convert.class);astMojo.importIfAbsent(element, SensitiveConverter.class);JCTree jcTree = trees.getTree(typeElem);jcTree.accept(new TreeTranslator() {@Overridepublic void visitVarDef(JCTree.JCVariableDecl jcVariableDecl) {List<JCTree.JCAnnotation> jcAnnotations = jcVariableDecl.mods.annotations;try {if (jcAnnotations != null && jcAnnotations.size() > 0) {List<JCTree.JCAnnotation> nil = List.nil();System.err.println(nil.toString());System.err.println("===============");for (JCTree.JCAnnotation jcAnnotation : jcAnnotations) {if (Sensitive.class.getName().equals(jcAnnotation.getAnnotationType().type.tsym.toString())) {JCTree.JCAnnotation converterAnnotation = treeMaker.Annotation(astMojo.select(Convert.class.getName()), List.of(treeMaker.Assign(treeMaker.Ident(names.fromString("converter")), treeMaker.Select(treeMaker.Ident(names.fromString(SensitiveConverter.class.getSimpleName())), names.fromString("class")))));nil = nil.append(converterAnnotation);} else {nil = nil.append(jcAnnotation);}}jcVariableDecl.mods.annotations = nil;System.err.println(nil.toString());}} catch (Exception e) {System.err.println(e);}try {super.visitVarDef(jcVariableDecl);} catch (Exception e) {System.err.println(e);}}});}return true;}@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);this.types = processingEnv.getTypeUtils();this.elements = processingEnv.getElementUtils();this.filer = processingEnv.getFiler();this.messager = processingEnv.getMessager();this.trees = JavacTrees.instance(processingEnv);Context context = ((JavacProcessingEnvironment) processingEnv).getContext();this.treeMaker = TreeMaker.instance(context);this.names = Names.instance(context);this.symtab = Symtab.instance(context);this.astMojo = new AstMojo(trees, treeMaker, names, symtab);}}
public class AstMojo {private final JavacTrees trees;private final TreeMaker treeMaker;private final Names names;private final Symtab symtab;public AstMojo(JavacTrees trees, TreeMaker treeMaker, Names names, Symtab symtab) {this.trees = trees;this.treeMaker = treeMaker;this.names = names;this.symtab = symtab;}public void importIfAbsent(Element element, Class<?> importType) {JCTree.JCCompilationUnit compilationUnit = ((JCTree.JCCompilationUnit) trees.getPath(element).getCompilationUnit());boolean contains = false;for (JCTree.JCImport jcImport : compilationUnit.getImports()) {if (importType.getName().equals(jcImport.getQualifiedIdentifier().toString())) {contains = true;break;}}if (!contains) {JCTree.JCIdent jcIdent = treeMaker.Ident(names.fromString(importType.getPackage().getName()));Name className = names.fromString(importType.getSimpleName());JCTree.JCFieldAccess jcFieldAccess = treeMaker.Select(jcIdent, className);JCTree.JCImport jcImport = treeMaker.Import(jcFieldAccess, false);compilationUnit.defs = compilationUnit.defs.prepend(jcImport);}}public JCTree.JCExpression select(String path) {JCTree.JCExpression expression = null;int i = 0;for (String split : path.split("\\.")) {if (i == 0)expression = treeMaker.Ident(names.fromString(split));else {expression = treeMaker.Select(expression, names.fromString(split));}i++;}return expression;}public JCTree.JCIdent identFromString(String name) {return treeMaker.Ident(names.fromString(name));}
}

注解处理器生效

在项目的resources 下新增文件META-INF/services/javax.annotation.processing.Processor
内容如下

net.reduck.sdp.processor.SensitiveProcessor

http://www.ppmy.cn/news/105080.html

相关文章

oop练习题

public static void main(String[] args) { // &#xff08;三&#xff09;无返回&#xff0c;有参数的方法&#xff1a; // 设置当前狗的姓名的方法&#xff0c;要求传入狗的新姓名&#xff0c;修改当前姓名。 // 设置当前狗的性别的方法&#xff0c;要求传入狗的新性别&am…

第三章 JVM内存概述

附录&#xff1a;精选面试题 Q&#xff1a;为什么虚拟机必须保证一个类的Clinit( )方法在多线程的情况下被同步加锁 &#xff1f; A: 因为虚拟机在加载完一个类之后直接把这个类放到本地内存的方法区&#xff08;也叫原空间&#xff09;中了&#xff0c;当其他程序再来调这个类…

【踩坑】mirai挂机运行经常自动退出怎么办?

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 背景介绍 解决思路 实现方法 最终效果 背景介绍 就是说&#xff0c;后台运行了mcl&#xff0c;但经常莫名其妙自动会退出&#xff0c;导致每次都得手动的去服务器上重新启动mcl。而对于自己运行的需要用…

Java制作520表白代码——爱一个人需要理由吗?

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;Java经典程序设计 520表白日&#xff0c;每个人都期待着浪漫的表白&#xff0c;而作为一名热爱编程的程序员&#xff0c;我决定用程序员的方式来向你表达我的爱意。 在2023年5月20日这个特殊的日子里&#xff0c;我要用一段特别的…

kong网关安装及konga安装

一、kong安装 安装机器地址&#xff1a;192.168.19.50 1、自定义一个docker网络 [rootmin ~]# docker network create kong-net a9bde4e7d16e4838992000cd5612476b238f7a88f95a07c994a9f57be7f64c10查看网络是否创建成功 [rootmin ~]# docker network ls NETWORK ID NA…

【计算机网络详解】——应用层(学习笔记)

&#x1f4d6; 前言&#xff1a;应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分。在本文中&#xff0c;我们以一些经典的网络应用为例来学习有关网络应用的原理、协议和实现方面的知识。 目录 &a…

Vue实例

1. 自定义元素 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

基于FPGA:运动目标检测(包围盒仿真工程,及一些调试问题)

目录 前言一、安装器件库二、仿真工程操作1、进入文件列表2、找到bounding_box_locate.vt&#xff0c;双击打开文件3、修改路径4、路径设置5、切换回“Hierarchy”&#xff0c;即工程界面6、运行仿真7、查看波形 重点&#xff1a;调试问题三、仿真代码1、仿真顶层文件2、绘制包…