最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot接口返回數(shù)據(jù)脫敏(Mybatis、Jackson)

 更新時間:2024年07月03日 11:24:54   作者:明天再去學(xué)習  
有時候,我們接口返回的數(shù)據(jù)需要做一些處理,有一些敏感數(shù)據(jù),本文主要介紹了SpringBoot接口返回數(shù)據(jù)脫敏(Mybatis、Jackson),具有一定的參考價值,感興趣的可以了解一下

一、前言

有時候,我們接口返回的數(shù)據(jù)需要做一些處理,有一些敏感數(shù)據(jù),我們不能全部返回給用戶,需要用*號隱藏掉一部分關(guān)鍵數(shù)據(jù),使得該敏感數(shù)據(jù)變得不完全,其他人無法知道脫敏前的數(shù)據(jù)是什么樣的。同時,存儲在底層數(shù)據(jù)庫的數(shù)據(jù),一些關(guān)鍵信息如用戶密碼、身份證、手機號等敏感信息,也不能夠通過明文的方式存放在數(shù)據(jù)庫中。

數(shù)據(jù)脫敏有以下幾種做法:

1、通過Mybatis處理

2、通過自定義Jackson注解,實現(xiàn)在屬性序列化過程中處理數(shù)據(jù)

3、其他方式

二、Mybatis數(shù)據(jù)脫敏

在數(shù)據(jù)庫中,根據(jù)業(yè)務(wù)需求存放了不少的有關(guān)用戶的敏感信息,比如身份證、手機、住址、密碼等信息,如果這些數(shù)據(jù)都是以明文的形式存放的,那么,當數(shù)據(jù)庫被破解后,用戶這些重要的信息都會被泄露。

數(shù)據(jù)加密解密很簡單,如果自己手動在插入數(shù)據(jù)前對數(shù)據(jù)加密,讀取到數(shù)據(jù)后進行手動解密,那么將會很麻煩。

因此,對于數(shù)據(jù)庫數(shù)據(jù)的加密解密將采用Mybatis的TypeHandler處理,在我們?yōu)閿?shù)據(jù)庫提供數(shù)據(jù)后,會根據(jù)我們的需求,對部分數(shù)據(jù)進行加密。在讀取后數(shù)據(jù)最終到我們手上前,會對讀取到數(shù)據(jù)進行解密。請給位跟隨以下的做法,實現(xiàn)Mybatis數(shù)據(jù)脫敏把。

1、自定義一個TypeHandler類型的處理器,用于處理數(shù)據(jù)的加密和解密

TypeHandler用于處理數(shù)據(jù)在數(shù)據(jù)庫類型和java類型之間的轉(zhuǎn)換,默認情況下,我們常用的String、Long、Date等java類型都有對應(yīng)的TypeHandler進行處理,我們自定義TypeHanler的情況在于目前沒有對應(yīng)的數(shù)據(jù)轉(zhuǎn)換處理器。

以下為自定義的字符串加密解密處理器:

import cn.hutool.crypto.symmetric.AES;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/**
 * mybatis String類型敏感字段處理類
 * 可以通過實現(xiàn)TypeHandler,但是BaseTypeHandler已經(jīng)實現(xiàn)了,我們可以繼承它
 */
public class SensitiveColumnHandler extends BaseTypeHandler<String> {

    private static final String key = "wjfgncvkdkd25fc2";

    /**
     * 設(shè)置參數(shù)值,在此處對字段值進行加密處理
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 如果字段或字段值為空,不進行處理
        if(StringUtils.isEmpty(parameter)){
            ps.setString(i, parameter);
            return;
        }
        // 對字段值進行加密,此處使用hutool工具,有其他使用其他即可
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        String secretStr = aes.encryptHex(parameter);
        ps.setString(i, secretStr);
    }

    /**
     * 獲取值內(nèi)容
     */
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String value = rs.getString(columnName);
        if(Objects.isNull(value)){
            return null;
        }
        // 對字段值進行加密,此處使用hutool工具,有其他使用其他即可
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        return aes.decryptStr(value);
    }

    /**
     * 獲取值內(nèi)容
     */
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String value = rs.getString(columnIndex);
        if(Objects.isNull(value)){
            return null;
        }
        // 對字段值就行加密,此處使用hutool工具,有其他使用其他即可
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        return aes.decryptStr(value);
    }

    /**
     * 獲取值內(nèi)容
     */
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String value = cs.getString(columnIndex);
        if(Objects.isNull(value)){
            return null;
        }
        // 對字段值進行加密,此處使用hutool工具,有其他使用其他即可
        AES aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));
        return aes.decryptStr(value);
    }
}

以上就是我們自定義一個TypeHandler類型的處理器,以下是關(guān)于處理器的使用,有分以下兩種情況:

1)、全局使用

全局使用需要在配置文件中指定處理器包位置,指定之后,在默認情況下,遇到該處理器能夠處理的類型,都將使用該處理器。不建議使用全局的方式使用自定義處理器,比如本文我們的自定義處理器是用于處理String字符串的,全局注冊處理器之后,所有的String值將會使用該處理器。但實際情況是,只有在字符串是敏感數(shù)據(jù)時,我們才需要用到自定義的處理器。

#指定TypeHandler處理器的包位置
type-handlers-package: com.sensitive.learn.handler

2)、局部使用

在我們需要的字段上使用typeHandler表明指定的處理器,沒有標注typeHandler的字段,將采用默認的處理器。(此處使用見Mapper.xml)。

2、創(chuàng)建Test實體類

@Data
@Accessors(chain = true)
public class Test {

    private Long id;

    private String idCard;

    private String phone;
    
}

3、創(chuàng)建TestMapper接口類

@Mapper
public interface TestMapper {

    List&lt;Test&gt; selectAll();

    Boolean insert(Test test);

}

4、Myatis Mapper文件

在需要自定義TypeHandler的字段是使用typeHandler屬性進行指定

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sensitive.learn.mapper.TestMapper">

    <resultMap id="testMap" type="com.sensitive.learn.model.Test">
        <id property="id" column="id"/>
        <result property="idCard" column="id_card" typeHandler="com.sensitive.learn.handler.SensitiveColumnHandler"/>
        <result property="phone" column="phone" typeHandler="com.sensitive.learn.handler.SensitiveColumnHandler"/>
    </resultMap>

    <select id="selectAll" resultMap="testMap">
        select id, id_card, phone
        from test
    </select>

    <insert id="insert" parameterType="com.sensitive.learn.model.Test">
        insert into test(id, id_card, phone)
        values(#{id},
               #{idCard, typeHandler=com.sensitive.learn.handler.SensitiveColumnHandler},
               #{phone, typeHandler=com.sensitive.learn.handler.SensitiveColumnHandler})
    </insert>

</mapper>

5、測試插入與查詢

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestClass {

    @Resource
    private TestMapper testMapper;

    @Test
    public void test1(){
        List<com.sensitive.learn.model.Test> tests = testMapper.selectAll();
        tests.forEach(System.out::println);

    }

    @Test
    public void test2(){
        com.sensitive.learn.model.Test test = new com.sensitive.learn.model.Test();
        test.setId(6L)
                .setIdCard("4493888665464654660")
                .setPhone("1234567890");
        testMapper.insert(test);
    }

}

插如之后查看數(shù)據(jù)庫表情況:

 可以看出數(shù)據(jù)已經(jīng)經(jīng)過加密了

查詢控制臺打印的結(jié)果:

Test(id=6, idCard=4493888665464654660, phone=1234567890)

可以看出能夠正常解密

好了,以上有關(guān)Mybatis的數(shù)據(jù)脫敏就到此為止了。

三、自定義Jackson數(shù)據(jù)脫敏

Jackson是Spring默認的序列化框架,以下將通過自定義Jackson注解,實現(xiàn)在序列化過程中對屬性值進行處理。

1、定義一個注解,標注在需要脫敏的字段上

import com.boot.learn.enums.SecretStrategy;
import com.boot.learn.serializer.SecretJsonSerializer;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

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)
@JacksonAnnotationsInside // 一般用于將其他的注解一起打包成"組合"注解
@JsonSerialize(using = SecretJsonSerializer.class) // 對標注注解的字段采用哪種序列化器進行序列化
public @interface SecretColumn {

    // 脫敏策略
    SecretStrategy strategy();

}

2、定義字段序列化策略,因為不同類型數(shù)據(jù)有不同脫敏后的展現(xiàn)形式。以下通過枚舉類方式實現(xiàn)幾種策略:

/**
 * 脫敏策略,不同數(shù)據(jù)可選擇不同的策略
 */
@Getter
public enum SecretStrategy {

    /**
     * 用戶名脫敏
     */
    USERNAME(str -> str.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),

    /**
     * 身份證脫敏
     */
    ID_CARD(str -> str.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),

    /**
     * 手機號脫敏
     */
    PHONE(str -> str.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),

    /**
     * 地址脫敏
     */
    ADDRESS(str -> str.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));

    private final Function<String, String> desensitizer;

    SecretStrategy(Function<String, String> desensitizer){
        this.desensitizer = desensitizer;
    }



}

3、定義一個Jackson序列化器,可以對標注了@SecretColumn 的注解進行處理

/**
 * 序列化器實現(xiàn)
 */
public class SecretJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private SecretStrategy secretStrategy;

    /**
     * 步驟一
     * 方法來源于ContextualSerializer,獲取屬性上的注解屬性,同時返回一個合適的序列化器
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        // 獲取自定義注解
        SecretColumn annotation = beanProperty.getAnnotation(SecretColumn.class);
        // 注解不為空,且標注的字段為String
        if(Objects.nonNull(annotation) && Objects.equals(String.class, beanProperty.getType().getRawClass())){
            this.secretStrategy = annotation.strategy();
            // 符合我們自定義情況,返回本序列化器,將順利進入到該類中的serialize()方法中
            return this;
        }
        // 注解為空,字段不為String,尋找合適的序列化器進行處理
        return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
    }

    /**
     * 步驟二
     * 方法來源于JsonSerializer<String>:指定返回類型為String類型,serialize()將修改后的數(shù)據(jù)返回
     */
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if(Objects.isNull(secretStrategy)){
            // 定義策略為空,返回原字符串
            jsonGenerator.writeString(s);
        }else {
            // 定義策略不為空,返回策略處理過的字符串
            jsonGenerator.writeString(secretStrategy.getDesensitizer().apply(s));
        }
    }
}

4、實體類中使用@SecretColumn注解

@ToString
@Data
@Accessors(chain = true)
public class User {

    /**
     * 真實姓名
     */
    @SecretColumn(strategy = SecretStrategy.USERNAME)
    private String realName;

    /**
     * 地址
     */
    @SecretColumn(strategy = SecretStrategy.ADDRESS)
    private String address;

    /**
     * 電話號碼
     */
    @SecretColumn(strategy = SecretStrategy.PHONE)
    private String phoneNumber;

    /**
     * 身份證號碼
     */
    @SecretColumn(strategy = SecretStrategy.ID_CARD)
    private String idCard;

}

5、測試

@RestController
@RequestMapping("/secret")
public class SecretController {

    @GetMapping("/test")
    public User test(){
        User user = new User();
        user.setRealName("陳平安")
                .setPhoneNumber("12345678910")
                .setAddress("浩然天下寶瓶洲驪珠洞天泥瓶巷")
                .setIdCard("4493888665464654659");
        System.out.println(user);
        return user;
    }

}

輸出結(jié)果:

1)控制臺打印結(jié)果

User(realName=陳平安, address=浩然天下寶瓶洲驪珠洞天泥瓶巷, phoneNumber=12345678910, idCard=4493888665464654659)

2)瀏覽器結(jié)果

{"realName":"陳*安","address":"浩然天****瓶洲驪珠洞天泥****","phoneNumber":"123****8910","idCard":"4493****54659"}

結(jié)論:通過自定義Jackson實現(xiàn)數(shù)據(jù)脫敏,猜測生效的過程是在接口返回對象數(shù)據(jù)時,序列化器將對象數(shù)據(jù)轉(zhuǎn)換成json數(shù)據(jù)時實現(xiàn)的。

可以在代碼中通過ObjectMapper序列化對象,同樣能夠得到脫敏后的數(shù)據(jù):

ObjectMapper objectMapper = new ObjectMapper();
try {
    System.out.println(objectMapper.writeValueAsString(user));
} catch (JsonProcessingException e) {
    e.printStackTrace();
}

控制臺打印結(jié)果:

{"realName":"陳*安","address":"浩然天****瓶洲驪珠洞天泥****","phoneNumber":"123****8910","idCard":"4493****54659"}

注意:如果使用的不是Jackson,而是fastjson或者其他的序列化工具,則需要使用定義對應(yīng)組件能夠識別的序列化器,否則,序列化將不生效。

四、總結(jié)

以上是關(guān)于接口數(shù)據(jù)脫敏兩個方面的實現(xiàn),一方面是通過Mybatis實現(xiàn),一方面是通過Jackson實現(xiàn),大家可以通過自己的業(yè)務(wù)需求靈活組合使用,事半功倍!

到此這篇關(guān)于SpringBoot接口返回數(shù)據(jù)脫敏(Mybatis、Jackson)的文章就介紹到這了,更多相關(guān)SpringBoot接口返回數(shù)據(jù)脫敏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot使用Jsp的示例代碼

    SpringBoot使用Jsp的示例代碼

    這篇文章主要介紹了SpringBoot使用Jsp的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • Nacos集群模式下服務(wù)無法注冊問題

    Nacos集群模式下服務(wù)無法注冊問題

    這篇文章主要介紹了Nacos集群模式下服務(wù)無法注冊問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java詳細解析==和equals的區(qū)別

    Java詳細解析==和equals的區(qū)別

    這篇文章主要介紹了Java中==和equals()的區(qū)別,,==可以使用在基本數(shù)據(jù)類型變量和引用數(shù)據(jù)類型變量中,equals()是方法,只能用于引用數(shù)據(jù)類型,需要的朋友可以參考下
    2022-04-04
  • Mybatis批量插入大量數(shù)據(jù)的最優(yōu)方式總結(jié)

    Mybatis批量插入大量數(shù)據(jù)的最優(yōu)方式總結(jié)

    批量插入功能是我們?nèi)粘9ぷ髦斜容^常見的業(yè)務(wù)功能之一,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Mybatis批量插入大量數(shù)據(jù)的幾種最優(yōu)方式,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-03-03
  • java實現(xiàn)發(fā)送郵件的示例代碼

    java實現(xiàn)發(fā)送郵件的示例代碼

    這篇文章主要介紹了java如何實現(xiàn)發(fā)送郵件,文中示例代碼非常詳細,幫助大家更好的理解和學(xué)習,感興趣的朋友可以了解下
    2020-07-07
  • java工具類static靜態(tài)方法讀取yml配置過程

    java工具類static靜態(tài)方法讀取yml配置過程

    文章介紹了在工具類中獲取YAML配置時遇到的問題,由于變量是靜態(tài)的,而Spring加載靜態(tài)方法比IOC容器早,導(dǎo)致無法直接使用@Value注解讀取YAML配置,從而讀取結(jié)果為null
    2024-11-11
  • Java使用貪心算法解決電臺覆蓋問題(示例詳解)

    Java使用貪心算法解決電臺覆蓋問題(示例詳解)

    貪心算法是指在對問題進行求解時,在每一步選擇中都采取最好或最優(yōu)的選擇,從而導(dǎo)致結(jié)果理想化,下面通過本文介紹下Java使用貪心算法解決電臺覆蓋問題,需要的朋友可以參考下
    2022-04-04
  • spring boot的攔截器簡單使用示例代碼

    spring boot的攔截器簡單使用示例代碼

    這篇文章主要介紹了spring boot的攔截器簡單使用實例代碼,需要的的朋友參考下吧
    2017-04-04
  • MyBatis分頁查詢返回list的時候出現(xiàn)null的問題

    MyBatis分頁查詢返回list的時候出現(xiàn)null的問題

    這篇文章主要介紹了MyBatis分頁查詢返回list的時候出現(xiàn)null的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • javascript checkbox全選和反選的簡單實現(xiàn)

    javascript checkbox全選和反選的簡單實現(xiàn)

    這篇文章主要介紹了javascript checkbox全選和反選的簡單實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評論

莆田市| 梁平县| 肇源县| 兴义市| 三江| 五家渠市| 天津市| 敦煌市| 连城县| 手机| 盐亭县| 银川市| 宜都市| 廊坊市| 诸城市| 吴江市| 那曲县| 宜良县| 巧家县| 宜州市| 宣汉县| 合川市| 同仁县| 阜新市| 滨海县| 抚松县| 屏边| 西充县| 镇平县| 德清县| 灵武市| 宁明县| 闵行区| 腾冲县| 临清市| 称多县| 铜鼓县| 榆社县| 卢氏县| 克拉玛依市| 宁远县|