龙空技术网

SpringBoot 实现数据加密脱敏(注解 + 反射 + AOP)

IT技术控 875

前言:

此时咱们对“c语言密码加密程序报告”都比较看重,看官们都想要学习一些“c语言密码加密程序报告”的相关内容。那么小编也在网摘上网罗了一些关于“c语言密码加密程序报告””的相关资讯,希望各位老铁们能喜欢,姐妹们快快来了解一下吧!

SpringBoot 实现数据加密脱敏(注解 + 反射 + AOP)

场景:响应政府要求,商业软件应保证用户基本信息不被泄露,不能直接展示用户手机号,身份证,地址等敏感信息。

根据上面场景描述,我们可以分析出两个点。

不被泄露说明用户信息应被加密储存;不能直接展示说明用户信息应脱敏展示;解决方案傻瓜式编程:将项目中关于用户信息实体类的字段,比如姓名,手机号,身份证,地址等,在新增进数据库之前,对数据进行加密处理;在列表中展示用户信息时,对数据库中的数据进行解密脱敏,然后返回给前端;切入式编程:将项目中关于用户信息实体类的字段用注解给标记,新增用户信息实体类(这里我们用UserBO来表示,给UserBO里面的name,phone字段添加@EncryptField),返回用户信息实体类(这里我们用UserDO来表示,给UserDO里面的name,phone字段添加@DecryptField);然后利用@EncryptField,@DecryptField做为切入点,以切面的形式实现加密,解密脱敏;

傻瓜式编程不是说傻,而是相当于切入式编程,傻瓜式编程需要对用户信息相关的所有接口进行加密,解密脱敏的逻辑处理,这里改动的地方就比较多,风险高,重复操作相同的逻辑,工作量大,后期不好维护;切入式编程只需要对用户信息字段添加注解,对有注解的字段统一进行加密,解密脱敏逻辑处理,操作方便,高聚合,易维护;

方案实现

傻瓜式编程没什么难度,这里我给大家有切入式编程来实现;在实现之前,跟大家预热一下注解,反射,AOP的知识;

注解实战创建注解

创建一个只能标记在方法上的注解:

java复制代码package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)         //METHOD 说明该注解只能用在方法上@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效public @interface Encryption {}

创建一个只能标记在字段上的注解:

java复制代码package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.FIELD)           //FIELD 说明该注解只能用在字段上@Retention(RetentionPolicy.RUNTIME)  //RUNTIME 说明该注解在运行时生效public @interface EncryptField {}

创建一个标记在字段上,且有值的注解:

java复制代码package com.weige.javaskillpoint.annotation;import com.weige.javaskillpoint.enums.DesensitizationEnum;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)public @interface DecryptField {	// 注解是可以有值的,这里可以为数组,String,枚举等类型	// DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value(); 这里的field是指当前标记的字段    DesensitizationEnum value(); }
注解使用

创建枚举

java复制代码package com.weige.javaskillpoint.enums;public enum DesensitizationEnum {    name,     // 用户信息姓名脱敏    address,  // 用户信息地址脱敏    phone;    // 用户信息手机号脱敏}

创建UserDO类

java复制代码package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.DecryptField;import com.weige.javaskillpoint.enums.DesensitizationEnum;import com.weige.javaskillpoint.utils.AesUtil;import java.lang.reflect.Field;// 用户信息返回实体类public class UserDO {    @DecryptField(DesensitizationEnum.name)    private String name;    @DecryptField(DesensitizationEnum.address)    private String address;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public UserDO(String name, String address) {        this.name = name;        this.address = address;    }    public static void main(String[] args) throws IllegalAccessException {        // 生成并初始化对象        UserDO userDO = new UserDO("梦想是什么","湖北省武汉市");        // 反射获取当前对象的所有字段        Field[] fields = userDO.getClass().getDeclaredFields();        // 遍历字段        for (Field field : fields) {            // 判断字段上是否存在@DecryptField注解            boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);            // 存在            if (hasSecureField) {                // 暴力破解 不然操作不了权限为private的字段                field.setAccessible(true);                // 如果当前字段在userDo中不为空 即name,address字段有值                if (field.get(userDO) != null) {                    // 获取字段上注解的value值                    DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();                    // 控制台输出                    System.out.println(desensitizationEnum);                    // 根据不同的value值 我们可以对字段进行不同逻辑的脱敏 比如姓名脱敏-魏*,手机号脱敏-187****2275                 }            }        }    }}
反射实战

创建UserBO类

java复制代码package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.EncryptField;import java.lang.reflect.Field;// 用户信息新增实体类public class UserBO {    @EncryptField    private String name;    @EncryptField    private String address;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public UserBO(String name, String address) {        this.name = name;        this.address = address;    }    @Override    public String toString() {        return "UserBO{" +                "name='" + name + '\'' +                ", address='" + address + '\'' +                '}';    }    public static void main(String[] args) throws IllegalAccessException {        UserBO userBO = new UserBO("周传雄","湖北省武汉市");        Field[] fields = userBO.getClass().getDeclaredFields();        for (Field field : fields) {            boolean annotationPresent = field.isAnnotationPresent(EncryptField.class);            if(annotationPresent){                // 当前字段内容不为空                if(field.get(userBO) != null){                    // 这里对字段内容进行加密                    Object obj = encrypt(field.get(userBO));                    // 字段内容加密过后 通过反射重新赋给该字段                    field.set(userBO, obj);                }            }        }        System.out.println(userBO);    }    public static Object encrypt(Object obj){        return "加密: " + obj;    }}
AOP实战

切入点:

java复制代码package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Encryption;import com.weige.javaskillpoint.entity.UserBO;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/encrypt")@Slf4jpublic class EncryptController {    @PostMapping("/v1")    @Encryption  // 切入点    public UserBO insert(@RequestBody UserBO user) {        log.info("加密后对象:{}", user);        return user;    }}

切面:

java复制代码package com.weige.javaskillpoint.aop;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;@Slf4j@Aspect@Componentpublic class EncryptAspect {    //拦截需加密注解 切入点    @Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")    public void point() {    }    @Around("point()") //环绕通知    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        //加密逻辑处理        encrypt(joinPoint);        return joinPoint.proceed();    }}

为什么这里要使用AOP:无论是注解,反射,都需要一个启动方法,我上面演示的是通过main函数来启动。使用AOP,项目启动后,只要调用切入点对应的方法,就会根据切入点来形成一个切面,进行统一的逻辑增强;如果大家熟悉SpringMVC,SpringMVC提供了 ResponseBodyAdvice 和 RequestBodyAdvice两个接口,这两个接口可以对请求和响应进行预处理,就可以不需要使用AOP;

加密解密脱敏实战

项目目录:

pom.xml文件:

java复制代码<dependencies>        <!--Springboot项目自带 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <!--Springboot Web项目 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--lombok -->        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <version>1.18.22</version>        </dependency>        <!-- hutool  -->        <dependency>            <groupId>cn.hutool</groupId>            <artifactId>hutool-all</artifactId>            <version>5.7.20</version>        </dependency>		<!-- 切面 aop  -->        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.9.7</version>        </dependency>    </dependencies>
实体类

用户信息新增实体类 :UserBO

java复制代码package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.EncryptField;// 实体类public class UserBO {    @EncryptField    private String name;    @EncryptField    private String address;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public UserBO(String name, String address) {        this.name = name;        this.address = address;    }    @Override    public String toString() {        return "UserBO{" +                "name='" + name + '\'' +                ", address='" + address + '\'' +                '}';    }}

用户信息返回实体类 :UserDO

java复制代码package com.weige.javaskillpoint.entity;import com.weige.javaskillpoint.annotation.DecryptField;import com.weige.javaskillpoint.enums.DesensitizationEnum;// 实体类public class UserDO {    @DecryptField(DesensitizationEnum.name)    private String name;    @DecryptField(DesensitizationEnum.address)    private String address;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public UserDO(String name, String address) {        this.name = name;        this.address = address;    }}
脱敏枚举
java复制代码package com.weige.javaskillpoint.enums;public enum DesensitizationEnum {    name,    address,    phone;}
注解

解密字段注解(字段):

java复制代码package com.weige.javaskillpoint.annotation;import com.weige.javaskillpoint.enums.DesensitizationEnum;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)public @interface DecryptField {    DesensitizationEnum value();}

解密方法注解(方法 作切入点):

java复制代码package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Decryption {}

加密字段注解(字段):

java复制代码package com.weige.javaskillpoint.annotation;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)public @interface EncryptField {}

加密方法注解(方法 作切入点):

java复制代码package com.weige.javaskillpoint.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Encryption {}
控制层

解密 Controller:

java复制代码package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Decryption;import com.weige.javaskillpoint.entity.UserDO;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/decrypt")public class DecryptController {    @GetMapping("/v1")    @Decryption    public UserDO decrypt() {        return new UserDO("7c29e296e92893476db5f9477480ba7f", "b5c7ff86ac36c01dda45d9ffb0bf73194b083937349c3901f571d42acdaa7bae");    }}

加密 Controller:

java复制代码package com.weige.javaskillpoint.controller;import com.weige.javaskillpoint.annotation.Encryption;import com.weige.javaskillpoint.entity.UserBO;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/encrypt")@Slf4jpublic class EncryptController {    @PostMapping("/v1")    @Encryption    public UserBO insert(@RequestBody UserBO user) {        log.info("加密后对象:{}", user);        return user;    }}
切面

解密脱敏切面:

java复制代码package com.weige.javaskillpoint.aop;import com.weige.javaskillpoint.annotation.DecryptField;import com.weige.javaskillpoint.enums.DesensitizationEnum;import com.weige.javaskillpoint.utils.AesUtil;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.Collection;import java.util.List;import java.util.Objects;@Slf4j@Aspect@Componentpublic class DecryptAspect {    //拦截需解密注解    @Pointcut("@annotation(com.weige.javaskillpoint.annotation.Decryption)")    public void point() {    }    @Around("point()")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        //解密        return decrypt(joinPoint);    }    public Object decrypt(ProceedingJoinPoint joinPoint) {        Object result = null;        try {            Object obj = joinPoint.proceed();            if (obj != null) {                //抛砖引玉 ,可自行扩展其他类型字段的判断                if (obj instanceof String) {                    decryptValue();                } else {                    result = decryptData(obj);                }            }        } catch (Throwable e) {            e.printStackTrace();        }        return result;    }    private Object decryptData(Object obj) throws IllegalAccessException {        if (Objects.isNull(obj)) {            return null;        }        if (obj instanceof ArrayList) {            decryptList(obj);        } else {            decryptObj(obj);        }        return obj;    }    private void decryptObj(Object obj) throws IllegalAccessException {        Field[] fields = obj.getClass().getDeclaredFields();        for (Field field : fields) {            boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);            if (hasSecureField) {                field.setAccessible(true);                if (field.get(obj) != null) {                    String realValue = (String) field.get(obj);                    DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();                    String value = (String) AesUtil.decrypt(realValue,desensitizationEnum);                    field.set(obj, value);                }            }        }    }    private void decryptList(Object obj) throws IllegalAccessException {        List<Object> result = new ArrayList<>();        if (obj instanceof ArrayList) {            result.addAll((Collection<?>) obj);        }        for (Object object : result) {            decryptObj(object);        }    }    private void decryptValue() {        log.info("根据对象进行解密脱敏,单个字段不做处理!");    }}

加密切面:

java复制代码package com.weige.javaskillpoint.aop;import com.weige.javaskillpoint.annotation.EncryptField;import com.weige.javaskillpoint.entity.UserBO;import com.weige.javaskillpoint.utils.AesUtil;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;import java.lang.reflect.Field;@Slf4j@Aspect@Componentpublic class EncryptAspect {    //拦截需加密注解    @Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")    public void point() {    }    @Around("point()")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        //加密        encrypt(joinPoint);        return joinPoint.proceed();    }    public void encrypt(ProceedingJoinPoint joinPoint) {        Object[] objects;        try {            objects = joinPoint.getArgs();            if (objects.length != 0) {                for (Object object : objects) {                    if (object instanceof UserBO) {                        Field[] fields = object.getClass().getDeclaredFields();                        for (Field field : fields) {                            if (field.isAnnotationPresent(EncryptField.class)) {                                field.setAccessible(true);                                if (field.get(object) != null) {                                    // 进行加密                                    Object o = field.get(object);                                    Object encrypt = AesUtil.encrypt(field.get(object));                                    field.set(object, encrypt);                                }                            }                        }                    }                }            }        } catch (Exception e) {            log.error(e.getMessage());        }    }}
工具类

加密工具类:AesUtil

java复制代码package com.weige.javaskillpoint.utils;import cn.hutool.core.util.CharsetUtil;import cn.hutool.crypto.SecureUtil;import cn.hutool.crypto.symmetric.AES;import com.weige.javaskillpoint.enums.DesensitizationEnum;public class AesUtil {    // 默认16位 或 128 256位    public static String AES_KEY = "Wk#qerdfdshbd910";    public static AES aes = SecureUtil.aes(AES_KEY.getBytes());    public static Object encrypt(Object obj) {        return aes.encryptHex((String) obj);    }    public static Object decrypt(Object obj, DesensitizationEnum desensitizationEnum) {        // 解密        Object decrypt = decrypt(obj);        // 脱敏        return DesensitizationUtil.desensitization(decrypt, desensitizationEnum);    }    public static Object decrypt(Object obj) {        return aes.decryptStr((String) obj, CharsetUtil.CHARSET_UTF_8);    }}

脱敏工具类:DesensitizationUtil

java复制代码package com.weige.javaskillpoint.utils;import cn.hutool.core.util.StrUtil;import com.weige.javaskillpoint.enums.DesensitizationEnum;public class DesensitizationUtil {    public static Object desensitization(Object obj, DesensitizationEnum desensitizationEnum) {        Object result;        switch (desensitizationEnum) {            case name:                result = strUtilHide(obj, 1);                break;            case address:                result = strUtilHide(obj, 3);                break;            default:                result = "";        }        return result;    }    /**     * start从0开始     */    public static Object strUtilHide(String obj, int start, int end) {        return StrUtil.hide(obj, start, end);    }    public static Object strUtilHide(Object obj, int start) {        return strUtilHide(((String) obj), start, ((String) obj).length());    }}
完结

以上代码不难,大伙复制到本地跑一遍,基本就能理解;愿每一位程序员少走弯路!

标签: #c语言密码加密程序报告