Springboot敏感字段脱敏的实现思路

生产环境用户的隐私数据,比如手机号、身份证或者一些账号配置等信息,入库时都要进行不落地脱敏,也就是在进入我们系统时就要实时的脱敏处理。

用户数据进入系统,脱敏处理后持久化到数据库,用户查询数据时还要进行反向解密。这种场景一般需要全局处理,那么用AOP切面来实现在适合不过了。

首先自定义两个注解@EncryptField@EncryptMethod分别用在字段属性和方法上,实现思路很简单,只要方法上应用到@EncryptMethod注解,则检查入参字段是否标注@EncryptField注解,有则将对应字段内容加密。

@Documented
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {

    String[] value() default "";
}
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptMethod {

    String type() default ENCRYPT;
}

切面的实现也比较简单,对入参加密,返回结果解密。

import com.xiaofu.annotation.EncryptField;
import com.xiaofu.annotation.EncryptMethod;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;

import java.lang.reflect.Field;
import java.util.*;

import static com.xiaofu.enums.EncryptConstant.DECRYPT;
import static com.xiaofu.enums.EncryptConstant.ENCRYPT;

@Slf4j
@Aspect
@Component
public class EncryptHandler {

    @Autowired
    private StringEncryptor stringEncryptor;

    @Pointcut("@annotation(com.xiaofu.annotation.EncryptMethod)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        /**
         * 加密
         */
        encrypt(joinPoint);
        /**
         * 解密
         */
        Object decrypt = decrypt(joinPoint);
        return decrypt;
    }

    public void encrypt(ProceedingJoinPoint joinPoint) {

        try {
            Object[] objects = joinPoint.getArgs();
            if (objects.length != 0) {
                for (Object o : objects) {
                    if (o instanceof String) {
                        encryptValue(o);
                    } else {
                        handler(o, ENCRYPT);
                    }
                    //TODO 其余类型自己看实际情况加
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public Object decrypt(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            Object obj = joinPoint.proceed();
            if (obj != null) {
                if (obj instanceof String) {
                    decryptValue(obj);
                } else {
                    result = handler(obj, DECRYPT);
                }
                //TODO 其余类型自己看实际情况加
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }

    private Object handler(Object obj, String type) throws IllegalAccessException {

        if (Objects.isNull(obj)) {
            return null;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
            if (hasSecureField) {
                field.setAccessible(true);
                String realValue = (String) field.get(obj);
                String value;
                if (DECRYPT.equals(type)) {
                    value = stringEncryptor.decrypt(realValue);
                } else {
                    value = stringEncryptor.encrypt(realValue);
                }
                field.set(obj, value);
            }
        }
        return obj;
    }

    public String encryptValue(Object realValue) {
        String value = null;
        try {
            value = stringEncryptor.encrypt(String.valueOf(realValue));
        } catch (Exception ex) {
            return value;
        }
        return value;
    }

    public String decryptValue(Object realValue) {
        String value = String.valueOf(realValue);
        try {
            value = stringEncryptor.decrypt(value);
        } catch (Exception ex) {
            return value;
        }
        return value;
    }
}

紧接着测试一下切面注解的效果,我们对字段mobileaddress加上注解@EncryptField做脱敏处理。

@EncryptMethod
@PostMapping(value = "test")
@ResponseBody
public Object testEncrypt(@RequestBody UserVo user, @EncryptField String name) {

    return insertUser(user, name);
}

private UserVo insertUser(UserVo user, String name) {
    System.out.println("加密后的数据:user" + JSON.toJSONString(user));
    return user;
}

@Data
public class UserVo implements Serializable {

    private Long userId;

    @EncryptField
    private String mobile;

    @EncryptField
    private String address;

    private String age;
}

请求这个接口,看到参数被成功加密,而返回给用户的数据依然是脱敏前的数据,符合我们的预期,那到这简单的脱敏实现就完事了。

到此这篇关于Springboot敏感字段脱敏的文章就介绍到这了,更多相关Springboot脱敏内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Springboot之日志、配置文件、接口数据如何脱敏

    目录 一.前言 二.配置文件如何脱敏? 1. 添加依赖 2. 配置秘钥 3. 生成加密后的数据 4. 将加密后的密文写入配置 5. 总结 三.接口返回数据如何脱敏? 1. 自定义一个Jackson注解 2. 定制脱敏策略 3. 定制JSON序列化实现 4. 定义Person类,对其数据脱敏 5. 模拟接口测试 6. 总结 四.日志文件如何数据脱敏? 1. 添加log4j2日志依赖 2. 在/resource目录下新建log4j2.xml配置 3. 自定义PatternLayout实现数据脱敏 4

  • Springboot敏感字段脱敏的实现思路

    生产环境用户的隐私数据,比如手机号.身份证或者一些账号配置等信息,入库时都要进行不落地脱敏,也就是在进入我们系统时就要实时的脱敏处理. 用户数据进入系统,脱敏处理后持久化到数据库,用户查询数据时还要进行反向解密.这种场景一般需要全局处理,那么用AOP切面来实现在适合不过了. 首先自定义两个注解@EncryptField.@EncryptMethod分别用在字段属性和方法上,实现思路很简单,只要方法上应用到@EncryptMethod注解,则检查入参字段是否标注@EncryptField注解,有则

  • springboot实现敏感字段加密存储解密显示功能

    springboot实现敏感字段加密存储,解密显示,通过mybatis,自定义注解+AOP切面,Base64加解密方式实现功能. 1.代码实现: 创建springboot项目 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <de

  • SpringBoot 自定义注解实现涉密字段脱敏

    目录 1. 创建隐私数据类型枚举:PrivacyTypeEnum 2. 创建自定义隐私注解:PrivacyEncrypt 3. 创建自定义序列化器:PrivacySerializer 4. 隐私数据隐藏工具类:PrivacyUtil 5. 注解使用 关于数据脱敏,网上的文章都是硬编码规则,比如对身份证,手机号,邮件地址等固定写法脱敏.本文在此基础上,拓展动态从数据库查出涉密关键字执行脱敏操作. 数据脱敏:把系统里的一些敏感数据进行加密处理后再返回,达到保护隐私作用,实现效果图如下: 其实要实现上

  • Springboot AOP对指定敏感字段数据加密存储的实现

    前言 本文主要内容: 1. 插入数据 自定义注解方式  对 指定接口方法 的 参数的指定字段进行 加密存储: 2.对数据内的加密数据,进行解密返回 先看看效果 : 数据存入数据库表内, 手机号phone和邮箱email 属于敏感数据,我们需要密文存储 : 查询解密返回: 1.  自定义注解 加密标识注解  NeedEncrypt.java : import java.lang.annotation.*; /** * @Author JCccc * @Description 需加密 * @Date

  • 使用自定义Json注解实现输出日志字段脱敏

    自定义Json注解实现输出日志字段脱敏 背景 在日志输出的时候,有时会输出一些用户的敏感信息,如手机号,身份证号,银行卡号等,现需要对这些信息在日志输出的时候进行脱敏处理 思路 使用fastjson的ValueFilter对带有自定义注解的字段进行过滤 /** * 敏感信息类型 * * @author worstEzreal * @version V1.0.0 * @date 2017/7/19 */ public enum SensitiveType { ID_CARD, BANK_CARD,

  • 使用mybatis拦截器处理敏感字段

    目录 mybatis拦截器处理敏感字段 前言 思路解析 代码 趟过的坑(敲黑板重点) mybatis Excutor 拦截器的使用 这里假设一个场景 实现过程的关键步骤和代码 重点 mybatis拦截器处理敏感字段 前言 由于公司业务要求,需要在不影响已有业务上对 数据库中已有数据的敏感字段加密解密,个人解决方案利用mybatis的拦截器加密解密敏感字段 思路解析 利用注解标明需要加密解密的entity类对象以及其中的数据 mybatis拦截Executor.class对象中的query,upd

  • mybatis-plus 拦截器敏感字段加解密的实现

    目录 背景 一.查询拦截器 二.插入和更新拦截器 三.注解 背景 数据库在保存数据时,对于某些敏感数据需要脱敏或者加密处理,如果一个一个的去加显然工作量大而且容易出错,这个时候可以考虑使用拦截器,本文针对的是mybatis-plus作为持久层框架,其他场景未测试.代码如下: 一.查询拦截器 package com.sfpay.merchant.service.interceptor; import com.baomidou.mybatisplus.core.toolkit.CollectionU

  • SpringBoot+Vue实现动态菜单的思路梳理

    目录 1. 整体思路 2. 前端渲染 3. 后端菜单生成 3.1 菜单表 3.2 菜单接口 关于 Spring Boot + Vue3 的动态菜单,松哥之前已经写了两篇文章了,这两篇文章主要是从代码上和大家分析动态菜单最终的实现方式,但是还是有小伙伴觉得没太看明白,感觉缺乏一个提纲挈领的思路,所以,今天松哥再整一篇文章和大家再来捋一捋这个问题,希望这篇文章能让小伙伴们彻底搞清楚这个问题. 1. 整体思路 首先我们来看整体思路. 光说思路大家还是云里雾里,我们结合具体的效果图来看: 最终菜单显示效

  • Log4j日志分类和过滤敏感字段的实例

    项目上线时,需要对项目做安全检查,其中有两项是对输出日志进行分类和过滤掉日志中敏感字段. 项目使用Log4j日志系统,下面简单介绍下这两项要求的实现方式. 对日志进行分类,要求调用其他服务的API日志按照格式单独输出到一个文件. 方式: 除根Logger外,再额外增加一个apiLogger,如下, <!-- api logger的设置--> <logger name="log4j.logger.apiLogger" additivity="false&quo

  • springboot整合websocket实现群聊思路代码详解

    实现思路 发送者向服务器发送大家早上好.其它客户端可以收到对应消息. 项目展示 通过springboot引入websocket,实现群聊,通过在线websocket测试进行展示. 核心代码 pom引入jar <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2

随机推荐