ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

系统中数据显示进行脱敏处理

2021-11-01 19:33:36  阅读:327  来源: 互联网

标签:String 处理 list 系统 field import null annotation 脱敏


情景分析

有时候在前端展示时,需要将电话号码,身份证等敏感信息过滤掉,显示成 *** 的字样;如果只是前端进行修改,那么其实数据还是会返回,只能进行后端的修改,

疑难点:

1:并不是所有页面都要进行模糊,比如管理员等操作不能进行模糊掉,

2:有部分导入的功能,导出的数据也可能需要模糊掉;新增时不能进行加密,修改有加密处理的,保存时需要再进行恢复;

3:一些返回的字段名字并不统一,有的叫 phoneNum,有的叫 phone,有点叫 managerPhone

4:部分前端组件比如客户下拉框等也包含身份证信息,同样需要进行脱敏处理;

5:返回结果进行处理时,可能是封装起来的对象,需要遍历加递归进行处理;

实现思路:

1:权限控制

设置页面给操作人员添加权限控制,哪些字段可以显示,哪些字段需要进行脱敏;

2:自定义注解,将需要进行模糊类型统一封装成一个实体,让需要脱敏的返回类型继承该实体,这样可以避免每一个实体中都去添加注解,然后进行AOP编程,将数据进行模糊处理;

切面的处理几乎每个都要搞,将其需要进行处理的权限字段放入到Redis中,修改权限控制时删除并更新Redis;

image-20211012152951449

@IgnoreEncrypt 注解,使用该注解标注的controller不会校验权限;

接口传入 ignoreEncrypt=1 或者使用 IgnoreEncrypt标注controller,都可以使该次请求不校验权限

@FieldRight注解:

@FieldRight(fieldRightType = FieldRightType.CARD_NO)
private String newcardNo;//证件号码,打*

实现代码:

1:声明脱敏的字段注解

/**
     * 标记字段 使用何种策略来脱敏
     */
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldRight {
    FieldRightType fieldRightType() default FieldRightType.OTHER;
}

其中脱敏类型可根据情况进行自定义

package com.xqc.commoncommon.enums;

public enum FieldRightType {

    /**
     * 证件号码
     */
    CARD_NO("cardNo","证件号码"),
    /**
     * 邮箱
     */
    EMAIL("email","邮箱"),
    /**
     * 联系电话
     */
    PHONE("phone","联系电话"),
    /**
     * 客户生日
     */
//    BIRTHDAY("birthday","客户生日"),
    /**
     * 联系地址
     */
    ADDRESS("address","联系地址"),
    /**
     * 证件地址
     */
    CARD_ADDRESS("cardAddress","证件地址"),
    /**
     * 注册地址
     */
    REGISTER_ADDRESS("registerAddress","注册地址"),
    /**
     * 工作地址
     */
    WORK_ADDRESS("workAddress","工作地址"),
    /**
     * 门户登录账号
     */
    LOGIN_NAME("loginName","门户登录账号"),
    /**
     * 其他(不受权限控制)
     */
    OTHER("other","其他"),
    
    private final String field;
    private final String fieldName;

    FieldRightType(String field, String fieldName) {
        this.field = field;
        this.fieldName = fieldName;
    }

    public String getField() {
        return field;
    }

    public String getFieldName() {
        return fieldName;
    }
}

2:自定义脱敏的统一实体

3:将需要脱敏的实体进行改造

4:对请求的统一处理

		//先从redis中查询权限,如果查询不到则从数据库中查询,并放在redis中
		//userId
		LoginUserInfo loginUserInfo = ServletUtil.getLoginUserInfo(request);
		String ignoreEncrypt = request.getParameter("ignoreEncrypt");
		if (loginUserInfo != null && !"1".equals(ignoreEncrypt)) {
			// 获取容器
			ServletContext sc = request.getSession().getServletContext();
			XmlWebApplicationContext cxt = (XmlWebApplicationContext) WebApplicationContextUtils.getWebApplicationContext(sc);
			// 从容器中获取DispersedCacheSerciceImpl
			if (cxt != null && cxt.getBean("DispersedCacheSerciceImpl") != null && iDispersedCacheSercice == null) {
				iDispersedCacheSercice = (DispersedCacheSerciceImpl) cxt.getBean("DispersedCacheSerciceImpl");
			}
			String key = "FieldRight:" + loginUserInfo.getUserId();
			Object o = iDispersedCacheSercice.get(key);
			List<String> userCustomerFieldRightList = new ArrayList<>();
			if (o != null) {
				userCustomerFieldRightList = JSONArray.parseArray(o.toString(), String.class);
			} else {
				UserCustomerFieldRightQuery query = new UserCustomerFieldRightQuery();
				query.setCompanyId(loginUserInfo.getCompanyId());
				query.setUserId(loginUserInfo.getUserId());
				//查询需要隐藏的字段
				query.setFieldRight(1);
				List<CommonUserCustomerFieldRightDTO> userCustomerFieldRight = iUserService.getUserCustomerFieldRight(query);
				//整理
				userCustomerFieldRightList = userCustomerFieldRight.stream().map(CommonUserCustomerFieldRightDTO::getField)
						.collect(Collectors.toList());
				iDispersedCacheSercice.add(key, userCustomerFieldRightList);
			}
			//查询该用户的客户权限,如果查询不到则表示全部放开
			request.setAttribute("userCustomerFieldRightList",userCustomerFieldRightList);
		}

5:AOP对返回结果统一处理

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.xqc.annotation.FieldRight;
import com.xqc.annotation.IgnoreEncrypt;
import com.xqc.enums.FieldRightType;
import com.xqc.utils.CommonUtil;

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

/**
 * controller的切面控制,目前用于字段权限控制<br/>
 * 关于字段权限控制,详情请查看{@link FieldRight}
 */
@Aspect
@Component
@Slf4j
public class CommonControllerAspect {
    /**
     * 切入所有添加{@link IgnoreEncrypt}注解的controller
     */
    @Pointcut("@annotation(com.xqc.annotation.IgnoreEncrypt)")
    public void pointcut(){}

    /**
     * 添加{@link IgnoreEncrypt}注解的controller在进入之前去除字段权限校验标志
     */
    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        requestAttributes.getRequest().setAttribute("userCustomerFieldRightList",null);
    }

    /**
     * 1、切入所有的controller
     * 2、目前(2021年8月4日)用于字段权限校验,需考虑该字段权限校验是否只过滤部分package下的controller
     */
    @Pointcut("execution(* com.xqc.*.controller.*.*(..))")
    public void allControllerPointCut(){}


    /**
     * 1、切入controller的返回后,
     * @param joinPoint
     * @param returnValue
     */
    @AfterReturning(value = "allControllerPointCut()", returning="returnValue")
    @SuppressWarnings("unchecked")
    public void afterController(JoinPoint joinPoint,Object returnValue) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        List<String> fieldRightList = (List<String>)requestAttributes.getRequest().getAttribute("userCustomerFieldRightList");
        try {
            dealFieldRight(returnValue,fieldRightList);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }


    /**
     * 字段权限处理主要逻辑
     * 1、 如果传入的是list,则遍历后递归
     * 2、 如果传入的是dto,则直接分析
     * 3、 传入的是其他类型,直接忽略
     */
    @SuppressWarnings("unchecked")
    public static boolean dealFieldRight(Object model, List<String> fieldRightList) throws Exception{
        //如果需要校验的字段,则直接返回false
        if (fieldRightList == null || fieldRightList.isEmpty()){
            return false;
        }
        //如果需要处理的object为空,则直接返回
        if (model == null){
            return false;
        }
        //根据typeName来判断是dto还是list
        String typeName = model.getClass().getTypeName();
        //判断是否是list,list的typeName:  java.util.***List,所以根据java.utll和小写的list为关键字进行判断,若同时出现则认定为是list
        if (typeName.contains("java.util") && typeName.toLowerCase().contains("list")){
            /*
                 是list
                 循环对每一个元素递归处理
             */
            List modelList = (List)model;
            if (modelList.isEmpty()){
                return false;
            }
            for (Object item : modelList) {
                if (item == null){
                    continue;
                }
                //递归进行处理,但是当第一次遇到的元素无需进行处理的时候,表示后续的item也无需处理了
                boolean canDoNext = dealFieldRight(item,fieldRightList);
                if (!canDoNext){
                    break;
                }
            }
            return true;
        }else if (typeName.contains("com.xqc")){
            //不是list,那么就根据com.xqc判断是不是项目的Object
            //需要循环读取父类,直到遇到Object为止:Object的superClass是空
            Class checkClass = model.getClass();
            while (checkClass.getSuperclass()!=null){
                Field[] fields = checkClass.getDeclaredFields();
                for (Field field : fields) {
                    //如果是一个list,那么需要递归进行处理
                    String type = field.getType().toString();
                    if (type.contains("java.util") && type.toLowerCase().contains("list")){
                        field.setAccessible(true);
                        List o = (List)field.get(model);
                        if (o == null){
                            continue;
                        }
                        if (o.isEmpty()){
                            continue;
                        }
                        for (Object item : o) {
                            if (item == null){
                                continue;
                            }
                            boolean canDoNext = dealFieldRight(item,fieldRightList);
                            if (!canDoNext){
                                break;
                            }
                        }
                    }
                    //如果field是dto,也要递归处理
                    if (type.contains("com.xqc")){
                        field.setAccessible(true);
                        Object o = field.get(model);
                        if (o != null){
                            dealFieldRight(model,fieldRightList);
                        }
                    }
                    String fieldName;
                    FieldRight annotation = field.getAnnotation(FieldRight.class);
                    if (annotation != null){
                        if (annotation.fieldRightType() == FieldRightType.OTHER){
                            continue;
                        }else{
                            fieldName = annotation.fieldRightType().getField();
                        }
                    }else {
                        //没有注解的不进行加密处理
                        continue;
                    }
                    if (fieldRightList.contains(fieldName)) {
                        field.setAccessible(true);
                        Object o1 = field.get(model);
                        if (o1 == null || "".equals(o1.toString())){
                            continue;
                        }
                        //如果是String就设置为星号,否则设置为空
                        if ("class java.lang.String".equals(field.getGenericType().toString())){
                            //判断是否是cardNo
                            if (fieldName.equals(FieldRightType.CARD_NO.getField())){
                                //获取cardNo
                                field.set(model, CommonUtil.cardNoSet(o1.toString()));
                            }else{
                            field.set(model, "******");

                            }
                        }else{
                            field.set(model,null);
                        }
                    }
                }
                checkClass = checkClass.getSuperclass();
            }
            return true;
        }else {
            //如果一个其他类型传进来进行数据处理,直接忽略即可,只需要处理list和com.xqc下的dto
            return false;
        }
    }
}

标签:String,处理,list,系统,field,import,null,annotation,脱敏
来源: https://www.cnblogs.com/nullering/p/15474414.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有