SpringBoot使用自定义注解实现数据脱敏过程详细解析

目录
  • 前言
  • 一、引入hutool工具类
  • 二、定义常用需要脱敏的数据类型的枚举
  • 三、定义脱敏方式枚举
  • 四、自定义脱敏的注解
  • 五、自定义Jackson的序列化方式
  • 六、使用
  • 七、脱敏效果

前言

对于某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作,例如银行卡号、身份证号、手机号等,脱敏方式有多种方式。可以修改SQL语句,也可以写硬代码,也可以修改JSON序列化,这里介绍通过修改Jackson序列化方式实现数据脱敏。

一、引入hutool工具类

maven:

<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
</dependency>

gradle:

// https://mvnrepository.com/artifact/cn.hutool/hutool-all
implementation group: 'cn.hutool', name: 'hutool-all', version: '5.8.5'

二、定义常用需要脱敏的数据类型的枚举

其中 OTHER类型为自定义类型,需在后面自定义脱敏的长度等。

package com.iscas.authentication.model.enums;
import lombok.Getter;
/**
 *
 * @version 1.0
 * @since jdk1.8
 */
@Getter
public enum PrivacyTypeEnum {
    /**
     * 中文名
     * */
    CHINESE_NAME,
    /**
     * 固话
     * */
    FIXED_PHONE,
    /**
     * 手机号
     * */
     MOBILE_PHONE,
    /**
     * 住址
     * */
    ADDRESS,
    /**
     * 密码
     * */
    PASSWORD,
    /**
     * 银行卡号
     * */
    BANK_CARD,
    /**
     * 邮箱
     * */
    EMAIL,
    /**
     * 身份证
     * */
    ID_CARD,
    /**
     * 其他类型
     * */
    OTHER;
}

三、定义脱敏方式枚举

其中,DEFAULT类型时,需要数据类型为上一步枚举中除OTHER外的已确定的类型,NONE表示不做脱敏,其他类型为注释的意思。

package com.iscas.authentication.model.enums;
/**
 *
 * @version 1.0
 * @since jdk1.8
 */
public enum DesensitizationTypeEnum {
    /**
     * 默认方式
     * */
    DEFAULT,
    /**
     * 头部脱敏
     * */
    HEAD,
    /**
     * 尾部脱敏
     * */
    TAIL,
    /**
     * 中间脱敏
     * */
    MIDDLE,
    /**
     * 头尾脱敏
     * */
    HEAD_TAIL,
    /**
     * 全部脱敏
     * */
    ALL,
    /**
     * 不脱敏,相当于没打这个注解
     * */
    NONE;
}

四、自定义脱敏的注解

其中,mode默认为DEFAULT,此时只需要设置dataType的类型为除OTHER外的确定类型即可,当mode不是DEFAULT或NONE时,根据不同的类型,headNoMaskLen等长度属性需要设置,见上面的注释的字面意思。

package com.iscas.authentication.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.iscas.authentication.model.enums.DesensitizationTypeEnum;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import com.iscas.authentication.service.DesensitizationSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 脱敏注解
 *
 * @version 1.0
 * @since jdk1.8
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerializer.class)
public @interface Desensitization {
    /**
     * 脱敏的隐私数据类型
     */
    PrivacyTypeEnum dataType();
    /**
     * 脱敏方式,默认方式不需要定义下面脱敏长度等信息,根据脱敏的隐私数据类型自动脱敏
     */
    DesensitizationTypeEnum mode() default DesensitizationTypeEnum.DEFAULT;
    /**
     * 尾部不脱敏的长度,当mode为HEAD或MIDDLE时使用
     */
    int tailNoMaskLen() default 1;
    /**
     * 头部不脱敏的长度,当mode为TAIL或MIDDLE时使用
     */
    int headNoMaskLen() default 1;
    /**
     * 中间不脱敏的长度,当mode为HEAD_TAIL时使用
     */
    int middleNoMaskLen() default 1;
    /**
     * 打码
     */
    char maskCode() default '*';
}

五、自定义Jackson的序列化方式

package com.iscas.authentication.service;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.iscas.authentication.annotation.Desensitization;
import com.iscas.authentication.model.enums.DesensitizationTypeEnum;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;
/**
 * 脱敏序列化类
 *
 * @author zhuquanwen
 * @version 1.0
 * @date 2023/1/5 9:24
 * @since jdk1.8
 */
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private Desensitization desensitization;
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(desensitize(s));
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                if (desensitization == null) {
                    desensitization = beanProperty.getContextAnnotation(Desensitization.class);
                }
                if (desensitization != null) {
                    return new DesensitizationSerializer(desensitization);
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }
    /**
     * 脱敏处理
     * */
    private String desensitize(String s) {
        if (StrUtil.isNotBlank(s)) {
            PrivacyTypeEnum dataType = desensitization.dataType();
            DesensitizationTypeEnum mode = desensitization.mode();
            switch (mode) {
                case DEFAULT:
                    // 默认方式,根据dataType自动选择脱敏方式
                    s = autoDesensitize(s, dataType);
                    break;
                case HEAD:
                    // 头部脱敏
                    s = headDesensitize(s);
                    break;
                case TAIL:
                    // 尾部脱敏
                    s = tailDesensitize(s);
                    break;
                case MIDDLE:
                    s = middleDesensitize(s);
                    break;
                case HEAD_TAIL:
                    s = headTailDesensitize(s);
                    break;
                case ALL:
                    s = allDesensitize(s);
                    break;
                case NONE:
                    // 不做脱敏
                    break;
                default:
            }
        }
        return s;
    }
    /**
     * 全部脱敏
     * */
    private String allDesensitize(String s) {
        return String.valueOf(desensitization.maskCode()).repeat(s.length());
    }
    /**
    * 头尾脱敏
    * */
    private String headTailDesensitize(String s) {
        int middleNoMaskLen = desensitization.middleNoMaskLen();
        if (middleNoMaskLen >= s.length()) {
            // 如果中间不脱敏的长度大于等于字符串的长度,不进行脱敏
            return s;
        }
        int len = s.length() - middleNoMaskLen;
        // 头部脱敏
        int headStart = 0;
        int headEnd = len / 2;
        s = StrUtil.replace(s, headStart, headEnd, desensitization.maskCode());
        // 尾部脱敏
        int tailStart = s.length() - (len - len / 2);
        int tailEnd = s.length();
        return StrUtil.replace(s, tailStart, tailEnd, desensitization.maskCode());
    }
    /**
     * 中间脱敏
     * */
    private String middleDesensitize(String s) {
        int headNoMaskLen = desensitization.headNoMaskLen();
        int tailNoMaskLen = desensitization.tailNoMaskLen();
        if (headNoMaskLen + tailNoMaskLen >= s.length()) {
            // 如果头部不脱敏的长度+尾部不脱敏长度 大于等于字符串的长度,不进行脱敏
            return s;
        }
        int start = headNoMaskLen;
        int end = s.length() - tailNoMaskLen;
        return StrUtil.replace(s, start, end, desensitization.maskCode());
    }
    /**
     * 尾部脱敏
     * */
    private String tailDesensitize(String s) {
        int headNoMaskLen = desensitization.headNoMaskLen();
        if (headNoMaskLen >= s.length()) {
            // 如果头部不脱敏的长度大于等于字符串的长度,不进行脱敏
            return s;
        }
        int start = headNoMaskLen;
        int end = s.length();
        return StrUtil.replace(s, start, end, desensitization.maskCode());
    }
    /**
     * 头部脱敏
     * */
    private String headDesensitize(String s) {
        int tailNoMaskLen = desensitization.tailNoMaskLen();
        if (tailNoMaskLen >= s.length()) {
            // 如果尾部不脱敏的长度大于等于字符串的长度,不进行脱敏
            return s;
        }
        int start = 0;
        int end = s.length() - tailNoMaskLen;
        return StrUtil.replace(s, start, end, desensitization.maskCode());
    }
    public static void main(String[] args) {
        System.out.println(StrUtil.replace("231085198901091813", 2, -10, '#'));
    }
    /**
     * 根据数据类型自动脱敏
     * */
    private String autoDesensitize(String s, PrivacyTypeEnum dataType) {
        switch (dataType) {
            case CHINESE_NAME:
                s = DesensitizedUtil.chineseName(s);
                break;
            case FIXED_PHONE:
                s = DesensitizedUtil.fixedPhone(s);
                break;
            case MOBILE_PHONE:
                s = DesensitizedUtil.mobilePhone(s);
                break;
            case ADDRESS:
                s = DesensitizedUtil.address(s, 8);
                break;
            case PASSWORD:
                s = DesensitizedUtil.password(s);
                break;
            case BANK_CARD:
                s = DesensitizedUtil.bankCard(s);
                break;
            case EMAIL:
                s = DesensitizedUtil.email(s);
                break;
            case ID_CARD:
                s = DesensitizedUtil.idCardNum(s, 1, 2);
                break;
            case OTHER:
                // 其他类型的不支持以默认方式脱敏,直接返回
                break;
            default:
        }
        return s;
    }
}

六、使用

下面是一个测试的例子:

package com.iscas.base.biz.test.controller;
import com.iscas.base.biz.desensitization.Desensitization;
import com.iscas.base.biz.desensitization.DesensitizationTypeEnum;
import com.iscas.base.biz.desensitization.PrivacyTypeEnum;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
 *
 * @author zhuquanwen
 * @version 1.0
 * @date 2023/1/6 8:40
 * @since jdk1.8
 */
@RestController
@RequestMapping("/test/desensitization")
public class TestDesensitizationController {
    @GetMapping
    public List<TestModel> test() {
        TestModel t1 = new TestModel();
        t1.setPassword("123456");
        t1.setEmail("zzz@163.com");
        t1.setPhone("137654879451");
        t1.setFixPhone("0453-4785462");
        t1.setBankCard("622648754896457");
        t1.setIdCard("245874563214578965");
        t1.setName("张王钊");
        t1.setAddress("北京市昌平区xxx街道xxx小区1-1-101");
        t1.setHeadStr("测试头部脱敏");
        t1.setTailStr("测试尾部脱敏");
        t1.setMiddleStr("测试中间脱敏");
        t1.setHeadTailStr("测试头尾脱敏");
        t1.setAllStr("测试全部脱敏");
        t1.setNoneStr("测试不脱敏");
        TestModel t2 = new TestModel();
        t2.setPassword("iscas123");
        t2.setEmail("xwg@sina.com");
        t2.setPhone("18547896547");
        t2.setFixPhone("010-62268795");
        t2.setBankCard("622648754896487");
        t2.setIdCard("100412547865478947");
        t2.setName("李二麻子");
        t2.setAddress("新疆省克拉玛依市xxx街道xxx小区1-1-101");
        t2.setHeadStr("测试头部脱敏");
        t2.setTailStr("测试尾部脱敏");
        t2.setMiddleStr("测试中间脱敏");
        t2.setHeadTailStr("测试头尾脱敏");
        t2.setAllStr("测试全部脱敏");
        t2.setNoneStr("测试不脱敏");
        return new ArrayList<>(){{
            add(t1);
            add(t2);
        }};
    }
    @Data
    private static class TestModel {
        /**
         * 模拟密码
         * */
        @Desensitization(dataType = PrivacyTypeEnum.PASSWORD)
        private String password;
        /**
         * 模拟邮箱
         * */
        @Desensitization(dataType = PrivacyTypeEnum.EMAIL)
        private String email;
        /**
         * 模拟手机号
         * */
        @Desensitization(dataType = PrivacyTypeEnum.MOBILE_PHONE)
        private String phone;
        /**
         * 模拟座机
         * */
        @Desensitization(dataType = PrivacyTypeEnum.FIXED_PHONE)
        private String fixPhone;
        /**
         * 模拟银行卡
         * */
        @Desensitization(dataType = PrivacyTypeEnum.BANK_CARD)
        private String bankCard;
        /**
         * 模拟身份证号
         * */
        @Desensitization(dataType = PrivacyTypeEnum.ID_CARD)
        private String idCard;
        /**
         * 模拟中文名
         * */
        @Desensitization(dataType = PrivacyTypeEnum.CHINESE_NAME)
        private String name;
        /**
         * 模拟住址
         * */
        @Desensitization(dataType = PrivacyTypeEnum.ADDRESS)
        private String address;
        /**
         * 模拟自定义脱敏-头部脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.HEAD, tailNoMaskLen = 4)
        private String headStr;
        /**
         * 模拟自定义脱敏-尾部脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.TAIL, headNoMaskLen = 4)
        private String tailStr;
        /**
         * 模拟自定义脱敏-中间脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.MIDDLE, headNoMaskLen = 2, tailNoMaskLen = 2)
        private String middleStr;
        /**
         * 模拟自定义脱敏-两头脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.HEAD_TAIL, middleNoMaskLen = 4)
        private String headTailStr;
        /**
         * 模拟自定义脱敏-全部脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.ALL)
        private String allStr;
        /**
         * 模拟自定义脱敏-不脱敏
         * */
        @Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.NONE)
        private String noneStr;
    }
}

下面是一个实际使用的例子如下,在tel、password、email上添加了@Desensitization注解,自定义的@TbField等注解请忽略

package com.iscas.authentication.model.sys;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.iscas.authentication.annotation.Desensitization;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import com.iscas.templet.annotation.table.TbField;
import com.iscas.templet.annotation.table.TbFieldRule;
import com.iscas.templet.annotation.table.TbSetting;
import com.iscas.templet.view.table.TableFieldType;
import com.iscas.templet.view.table.TableSearchType;
import com.iscas.templet.view.table.TableViewType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.List;
/**
 * @author zhuquanwen
 * @version 1.0
 * @date 2022/3/11 21:23
 * @since jdk11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(title = "用户")
@TableName(value = "oauth_sys_user")
@Accessors(chain = true)
@TbSetting(title = "用户", checkbox = true, viewType = TableViewType.multi)
public class User extends BaseEntity {
    @TableId(type = IdType.AUTO)
    @Schema(title = "id")
    @TbField(field = "id", header = "id",
            type = TableFieldType.text, hidden = true)
    private Integer id;
    @Schema(title = "用户名")
    @TbField(field = "name", header = "名称", search = true, searchType = TableSearchType.like,
            type = TableFieldType.text, rule=@TbFieldRule(required = true, minLength = 2, maxLength = 20, distinct = true, desc = "用户名不能为空,且长度介于2-20个字符之间"))
    private String name;
    @Schema(title = "密码")
    @TbField(field = "password", header = "密码", hidden = true, editable = false,
            type = TableFieldType.text)
    @Desensitization(dataType = PrivacyTypeEnum.PASSWORD)
    private String password;
    @Schema(title = "type")
    @TbField(field = "type", header = "用户类型", search = true, searchType = TableSearchType.exact,
            type = TableFieldType.select, option = "[{\"label\":\"正常用户\",\"value\":\"1\"},{\"label\":\"战位IP用户\",\"value\":\"2\"}]")
    private String type;
    @Schema(title = "status")
    @TbField(field = "status", header = "状态", search = true, searchType = TableSearchType.exact,
            type = TableFieldType.select, option = "[{\"label\":\"正常\",\"value\":\"1\"},{\"label\":\"禁用\",\"value\":\"0\"}]")
    private String status;
    @Schema(title = "真实姓名")
    @TbField(field = "realName", header = "真实姓名",
            type = TableFieldType.text, rule=@TbFieldRule(required = true, minLength = 2, maxLength = 20, desc = "真实姓名不能为空,且长度介于2-20个字符之间"))
    private String realName;
    @Schema(title = "电话号码")
    @TbField(field = "tel", header = "电话号码",
            type = TableFieldType.text, rule=@TbFieldRule(reg = "^(13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\\d{8}$", desc = "电话号码需符规则"))
    @Desensitization(dataType = PrivacyTypeEnum.MOBILE_PHONE)
    private String tel;
    @Schema(title = "邮箱")
    @TbField(field = "email", header = "邮箱",
            type = TableFieldType.text, rule=@TbFieldRule(reg = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*.\\w+([-.]\\w+)*$", desc = "邮箱需符规则"))
    @Desensitization(dataType = PrivacyTypeEnum.EMAIL)
    private String email;
    @Schema(title = "部门")
    @TbField(field = "orgIds", header = "部门",
            type = TableFieldType.multiSelect, selectUrl = "/api/v1/orgs/combobox/tree?status=1")
    @TableField(exist = false)
    private List<Integer> orgIds;
    @Schema(title = "角色")
    @TbField(field = "roleIds", header = "角色",
            type = TableFieldType.multiSelect, selectUrl = "/api/v1/roles/combobox?status=1")
    @TableField(exist = false)
    private List<Integer> roleIds;
    @Schema(title = "岗位")
    @TbField(field = "postIds", header = "岗位",
            type = TableFieldType.multiSelect, selectUrl = "/api/v1/posts/combobox?status=1")
    @TableField(exist = false)
    private List<Integer> postIds;
}

七、脱敏效果

下面是测试的结果:

下面是一个查询接口返回带User实体的结果:

到此这篇关于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使用自定义注解实现数据脱敏过程详细解析

    目录 前言 一.引入hutool工具类 二.定义常用需要脱敏的数据类型的枚举 三.定义脱敏方式枚举 四.自定义脱敏的注解 五.自定义Jackson的序列化方式 六.使用 七.脱敏效果 前言 对于某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作,例如银行卡号.身份证号.手机号等,脱敏方式有多种方式.可以修改SQL语句,也可以写硬代码,也可以修改JSON序列化,这里介绍通过修改Jackson序列化方式实现数据脱敏. 一.引入hutool工具类 maven: <dependency> <g

  • SpringBoot通过自定义注解实现参数校验

    目录 1. 为什么要进行参数校验 2. 如何实现参数校验 3. 注解实现参数校验 4. 自定义注解实现参数校验 1. 为什么要进行参数校验 在后端进行工作时,需要接收前端传来的数据去数据库查询,但是如果有些数据过于离谱,我们就可以直接把它pass掉,不让这种垃圾数据接触数据库,减小数据库的压力. 有时候会有不安分的人通过一些垃圾数据攻击咱们的程序,让咱们的服务器或数据库崩溃,这种攻击虽然低级但不得不防,就像QQ进行登录请求时,它们向后端发送 账号=123,密码=123 的数据,一秒钟还发1w次,

  • SpringBoot中自定义注解实现参数非空校验的示例

    前言 由于刚写项目不久,在写 web 后台接口时,经常会对前端传入的参数进行一些规则校验,如果入参较少还好,一旦需要校验的参数比较多,那么使用 if 校验会带来大量的重复性工作,并且代码看起来会非常冗余,所以我首先想到能否通过一些手段改进这点,让 Controller 层减少参数校验的冗余代码,提升代码的可阅读性. 经过阅读他人的代码,发现使用 annotation 注解是一个比较方便的手段,SpringBoot 自带的 @RequestParam 注解只会校验请求中该参数是否存在,但是该参数是

  • SpringBoot通过自定义注解实现日志打印的示例代码

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • SpringBoot通过自定义注解与异步来管理日志流程

    目录 一.前言 二.基础环境 1. 导入依赖 2. 编写yml配置 三.数据库设计 四.主要功能 1. 编写注解 2. 业务类型枚举 3. 编写切片 4. ip工具类 5. 事件发布 6. 监听者 五.测试 1. controller 2. service 3. dao 4. 测试 5. 数据库 六.总结 一.前言 我们在企业级的开发中,必不可少的是对日志的记录,实现有很多种方式,常见的就是基于AOP+注解进行保存,同时考虑到程序的流畅和效率,我们可以使用异步进行保存! 二.基础环境 1. 导入

  • SpringBoot使用自定义注解实现权限拦截的示例

    本文介绍了SpringBoot使用自定义注解实现权限拦截的示例,分享给大家,具体如下: HandlerInterceptor(处理器拦截器) 常见使用场景 日志记录: 记录请求信息的日志, 以便进行信息监控, 信息统计, 计算PV(page View)等 性能监控: 权限检查: 通用行为: 使用自定义注解实现权限拦截 首先HandlerInterceptor了解 在HandlerInterceptor中有三个方法: public interface HandlerInterceptor { //

  • SpringBoot使用自定义注解+AOP+Redis实现接口限流的实例代码

    目录 为什么要限流 限流背景 实现限流 1.引入依赖 2.自定义限流注解 3.限流切面 4.写一个简单的接口进行测试 5.全局异常拦截 6.接口测试 为什么要限流 系统在设计的时候,我们会有一个系统的预估容量,长时间超过系统能承受的TPS/QPS阈值,系统有可能会被压垮,最终导致整个服务不可用.为了避免这种情况,我们就需要对接口请求进行限流. 所以,我们可以通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系统或避免不必要的资源浪费,一旦达到限制速率则可以拒绝服务.排队或等待

  • SpringBoot通过自定义注解实现配置类的自动注入的实现

    目录 前言 实现思路 自定义配置类读取配置 自定义注解 创建子配置Bean 通过反射进行people bean的注入 使用 效果 回顾 延伸 前言 SpringBoot中通过@ConfigurationProperties或@Value注解就可以获取配置文件中的属性定义并绑定到Java Bean或属性上,这也是我们平常使用最多的一种方式.但是小胖在开发过程中就遇到一个问题:在做MQ的开发中,配置文件中会配置多个生产者分别提供不同的业务能力,如果通过@ConfigurationProperties

  • SpringBoot + Spring Cloud Consul 服务注册和发现详细解析

    什么是Consul Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其它分布式服务注册与发现的方案,Consul 的方案更"一站式",内置了服务注册与发现框架.分布一致性协议实现.健康检查.Key/Value 存储.多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等).使用起来也较为简单.Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux.windows和Mac OS X):安装包仅包含一个可执行文件

  • SpringBoot中自定义注解实现控制器访问次数限制实例

    今天给大家介绍一下SpringBoot中如何自定义注解实现控制器访问次数限制. 在Web中最经常发生的就是利用恶性URL访问刷爆服务器之类的攻击,今天我就给大家介绍一下如何利用自定义注解实现这类攻击的防御操作. 其实这类问题一般的解决思路就是:在控制器中加入自定义注解实现访问次数限制的功能. 具体的实现过程看下面的例子: 步骤一:先定义一个注解类,下面看代码事例: package example.controller.limit; import org.springframework.core.

随机推荐