日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

1.概述

近來(lái)我們都在圍繞著使用Spring Boot開(kāi)發(fā)業(yè)務(wù)系統(tǒng)時(shí)如何保證數(shù)據(jù)安全性這個(gè)主題展開(kāi)總結(jié),當(dāng)下大部分的B/S架構(gòu)的系統(tǒng)也都是基于Spring Boot + SpringMVC三層架構(gòu)開(kāi)發(fā)的,可以認(rèn)為是在SpringMVC的三層架構(gòu)中的controller層(邏輯控制層)對(duì)接口數(shù)據(jù)進(jìn)行安全處理操作,更直接點(diǎn)說(shuō)就是在接口請(qǐng)求參數(shù)傳入進(jìn)行邏輯處理或者響應(yīng)參數(shù)輸出到頁(yè)面展示之前進(jìn)行數(shù)據(jù)處理的,所以只是在SpringMVC三層架構(gòu)中的一層中進(jìn)行安全加固,還不是很穩(wěn)固,接下來(lái)今天我們就再來(lái)講講在SpringMVC三層架構(gòu)另一層中如何進(jìn)行數(shù)據(jù)安全加固,在進(jìn)入今天主題之前先來(lái)看看什么是SpringMVC架構(gòu)?

什么是SpringMVC三層架構(gòu)?

SpringMVC的工程結(jié)構(gòu)一般來(lái)說(shuō)分為三層,自下而上是Modle層(模型,數(shù)據(jù)訪問(wèn)層)、Cotroller層(控制,邏輯控制層)、View層(視圖,頁(yè)面顯示層),其中Modle層分為兩層:dao層、service層,MVC架構(gòu)分層的主要作用是解耦。采用分層架構(gòu)的好處,普遍接受的是系統(tǒng)分層有利于系統(tǒng)的維護(hù),系統(tǒng)的擴(kuò)展。就是增強(qiáng)系統(tǒng)的可維護(hù)性和可擴(kuò)展性。對(duì)于Spring這樣的框架,(ViewWeb)表示層調(diào)用控制層(Controller),控制層調(diào)用業(yè)務(wù)層(Service),業(yè)務(wù)層調(diào)用數(shù)據(jù)訪問(wèn)層(Dao) 可以這么說(shuō),現(xiàn)在90%以上的業(yè)務(wù)系統(tǒng)都是基于該三層架構(gòu)模式開(kāi)發(fā)的,這種架構(gòu)模式也有人說(shuō)是設(shè)計(jì)模式中一種,可見(jiàn)其重要性不言而喻,所以我們需重視。

我們也都知道在日常開(kāi)發(fā)系統(tǒng)過(guò)程中,數(shù)據(jù)安全是非常重要的。特別是在當(dāng)今互聯(lián)網(wǎng)時(shí)代,個(gè)人隱私安全極其重要,一旦個(gè)人用戶數(shù)據(jù)遭到攻擊泄露,將會(huì)造成災(zāi)難級(jí)的事故問(wèn)題。所有之前我們基于接口層進(jìn)行數(shù)據(jù)安全處理是遠(yuǎn)遠(yuǎn)不夠的,今天我們就來(lái)談?wù)勅绾蜯odel層(數(shù)據(jù)訪問(wèn)層)怎樣做到優(yōu)雅數(shù)據(jù)加密存儲(chǔ)、模糊匹配及其脫敏展示,本文的主題:數(shù)據(jù)加密存儲(chǔ)、模糊匹配和脫敏展示。

銀行系統(tǒng)對(duì)數(shù)據(jù)安全性的要求在業(yè)務(wù)系統(tǒng)中是首屈一指的,所以今天我們就以常見(jiàn)的個(gè)人銀行賬戶數(shù)據(jù):密碼、手機(jī)號(hào)、詳細(xì)地址、銀行卡號(hào)等信息字段為例,進(jìn)行主題的宣講與淺析。

項(xiàng)目推薦:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企業(yè)級(jí)系統(tǒng)架構(gòu)底層框架封裝,解決業(yè)務(wù)開(kāi)發(fā)時(shí)常見(jiàn)的非功能性需求,防止重復(fù)造輪子,方便業(yè)務(wù)快速開(kāi)發(fā)和企業(yè)技術(shù)棧框架統(tǒng)一管理。引入組件化的思想實(shí)現(xiàn)高內(nèi)聚低耦合并且高度可配置化,做到可插拔。嚴(yán)格控制包依賴和統(tǒng)一版本管理,做到最少化依賴。注重代碼規(guī)范和注釋,非常適合個(gè)人學(xué)習(xí)和企業(yè)使用

Github地址:github.com/plasticene/…

Gitee地址:gitee.com/plasticene3…

2.數(shù)據(jù)加密存儲(chǔ)

我們之前總結(jié)的是在接口層進(jìn)行數(shù)據(jù)加解密傳輸,也強(qiáng)調(diào)過(guò)這種方式保證不了數(shù)據(jù)的絕對(duì)安全,只是有效提高接口數(shù)據(jù)安全性,抬高數(shù)據(jù)被抓取的門(mén)檻而已。所以接下來(lái)我們就來(lái)講述一下如何在數(shù)據(jù)的源頭存儲(chǔ)層保障其安全。我們都知道一些核心私密字段,比如說(shuō)密碼,手機(jī)號(hào)等在數(shù)據(jù)庫(kù)層存儲(chǔ)就不能明文存儲(chǔ),必須加密存儲(chǔ)保證即使數(shù)據(jù)庫(kù)泄露了也不會(huì)輕易曝光數(shù)據(jù)。

2.1 優(yōu)雅實(shí)現(xiàn)數(shù)據(jù)庫(kù)字段加解密原理

MyBatis-plus提供企業(yè)高級(jí)特性就有支持?jǐn)?shù)據(jù)加密解密,不過(guò)是收費(fèi)的。。。但是我們可以細(xì)細(xì)探究其原理進(jìn)行功能的自我實(shí)現(xiàn)。

其實(shí)在我們上面推薦的快速開(kāi)發(fā)框架中就已經(jīng)優(yōu)雅整合了數(shù)據(jù)加解密功能了,EncryptTypeHandler:實(shí)現(xiàn)數(shù)據(jù)庫(kù)的字段加密與解密。

默認(rèn)提供了基于base64加密算法Base64EncryptService和AES加密算法AESEncryptService,當(dāng)然業(yè)務(wù)側(cè)也可以自定義加密算法,這需要實(shí)現(xiàn)接口EncryptService,并把實(shí)現(xiàn)類注入到容器中即可。加密功能核心邏輯

@Bean
@ConditionalOnMissingBean(EncryptService.class)
public EncryptService encryptService() {
  Algorithm algorithm = encryptProperties.getAlgorithm();
  EncryptService encryptService;
  switch (algorithm) {
    case BASE64:
      encryptService =  new Base64EncryptService();
      break;
    case AES:
      encryptService = new AESEncryptService();
      break;
    default:
      encryptService =  null;
  }
  return encryptService;
}

接下來(lái)就可以基于加密算法,擴(kuò)展mybatis的typeHandler對(duì)實(shí)體字段數(shù)據(jù)進(jìn)行加密解密了:EncryptTypeHandler

public class EncryptTypeHandler<T> extends BaseTypeHandler<T> {

    @Resource
    private EncryptService encryptService;

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, encryptService.encrypt((String)parameter));
    }
    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String columnValue = rs.getString(columnName);
        return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String columnValue = rs.getString(columnIndex);
        return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String columnValue = cs.getString(columnIndex);
        return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);
    }
}

2.2 加密與解密示例

首先創(chuàng)建一張user表:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `phone` varchar(255) DEFAULT NULL COMMENT '手機(jī)號(hào)',
  `id_card` varchar(255) DEFAULT NULL COMMENT '身份證號(hào)',
  `bank_card` varchar(255) DEFAULT NULL COMMENT '銀行卡號(hào)',
  `address` varchar(255) DEFAULT NULL COMMENT '住址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

這時(shí)候我們正常插入一條數(shù)據(jù):

    @Test
    public void test() {
        User user = new User();
        user.setName("shepherd");
        user.setMobile("17812345678");
        user.setIdCard("213238199601182111");
        user.setBankCard("3222022046741500");
        user.setAddress("杭州市余杭區(qū)未來(lái)科技城");
        userDAO.insert(user);
    }

數(shù)據(jù)庫(kù)存儲(chǔ)查詢結(jié)果如下:

id

name

mobile

id_card

bank_card

address

1567402046481436673

shepherd

17812345678

213238199601182111

3222022046741500

杭州市余杭區(qū)未來(lái)科技城

這就是我們平時(shí)不加密存儲(chǔ)查詢的結(jié)果,這里id是通過(guò)分布式id算法自動(dòng)生成的哈。

接下來(lái)我們來(lái)看看實(shí)現(xiàn)對(duì)數(shù)據(jù)的加密,只需要在配置文件配置使用哪一種加密算法和在實(shí)體類的字段屬性加上注解@TableField(typeHandler = EncryptTypeHandler.class)即可。

這里我們使用aes加密算法:

ptc:
  encrypt:
    algorithm: aes

實(shí)體類:

@Data
@TableName(autoResultMap = true)
public class User {

    private Long id;
    private String name;

    @TableField(typeHandler = EncryptTypeHandler.class)
    private String mobile;
    @TableField(typeHandler = EncryptTypeHandler.class)
    private String idCard;
    @TableField(typeHandler = EncryptTypeHandler.class)
    private String bankCard;
    @TableField(typeHandler = EncryptTypeHandler.class)
    private String address;
}

再次插入數(shù)據(jù),數(shù)據(jù)庫(kù)存儲(chǔ)查詢結(jié)果如下:

id

name

mobile

id_card

bank_card

address

1567405175268642818

shepherd

9MgWngwLcd/vbYYYpG9pGQ==

97vlZQahK+y548ofQbXlW9JUwuzuj3xCkNF/is1KLa4=

2oQv5+y4+rVyN23IzudtOz+Zd7Aj1Bv2toBzmnwTXxo=

0Wj7qqLl6jWkBu+TcxuwGYcdIjv+zIJHDM7d1dU/c8D2jc2wLp+zVvpSwBKWjX44

然后我們可以測(cè)試對(duì)這條數(shù)據(jù)進(jìn)行查詢:

    @Test
    public void get() {
        User user = userDAO.selectById(1567405175268642818l);
        System.out.println(user);
    }

結(jié)果如下:

User(id=1567405175268642818, name=shepherd, mobile=17812345678, idCard=213238199601182111, bankCard=3222022046741500, address=杭州市余杭區(qū)未來(lái)科技城)

基于以上完美展示了數(shù)據(jù)加密存儲(chǔ)和解密查詢。

2.3 數(shù)據(jù)加密后怎么進(jìn)行模糊匹配

密碼、手機(jī)號(hào)、詳細(xì)地址、銀行卡號(hào)這些信息對(duì)加解密的要求也不一樣,比如說(shuō)密碼我們需要加密存儲(chǔ),一般使用的都是不可逆的慢hash算法,慢hash算法可以避免暴力破解(典型的用時(shí)間換安全性)。

在檢索時(shí)我們既不需要解密也不需要模糊查找,直接使用密文完全匹配,但是手機(jī)號(hào)就不能這樣做,因?yàn)槭謾C(jī)號(hào)我們要查看原信息,并且對(duì)手機(jī)號(hào)還需要支持模糊查找,因此我們今天就針對(duì)可逆加解密的數(shù)據(jù)支持模糊查詢來(lái)看看有哪些實(shí)現(xiàn)方式。

我們接下來(lái)看看常規(guī)的做法,也是最廣泛使用的方法,此類方法及滿足的數(shù)據(jù)安全性,又對(duì)查詢友好。

  • 在數(shù)據(jù)庫(kù)實(shí)現(xiàn)加密算法函數(shù),在模糊查詢的時(shí)候使用decode(key) like '%partial%

在數(shù)據(jù)庫(kù)中實(shí)現(xiàn)與程序一致的加解密算法,修改模糊查詢條件,使用數(shù)據(jù)庫(kù)加解密函數(shù)先解密再模糊查找,這樣做的優(yōu)點(diǎn)是實(shí)現(xiàn)成本低,開(kāi)發(fā)使用成本低,只需要將以往的模糊查找稍微修改一下就可以實(shí)現(xiàn),但是缺點(diǎn)也很明顯,這樣做無(wú)法利用數(shù)據(jù)庫(kù)的索引來(lái)優(yōu)化查詢,甚至有一些數(shù)據(jù)庫(kù)可能無(wú)法保證與程序?qū)崿F(xiàn)一致的加解密算法,但是對(duì)于常規(guī)的加解密算法都可以保證與應(yīng)用程序一致。如果對(duì)查詢性能要求不是特別高、對(duì)數(shù)據(jù)安全性要求一般,可以使用常見(jiàn)的加解密算法比如說(shuō)AES、DES之類的也是一個(gè)不錯(cuò)的選擇。

  • 對(duì)密文數(shù)據(jù)進(jìn)行分詞組合,將分詞組合的結(jié)果集分別進(jìn)行加密,然后存儲(chǔ)到擴(kuò)展列,查詢時(shí)通過(guò)key like '%partial%' [先對(duì)字符進(jìn)行固定長(zhǎng)度的分組,將一個(gè)字段拆分為多個(gè),比如說(shuō)根據(jù)4位英文字符(半角),2個(gè)中文字符(全角)為一個(gè)檢索條件,舉個(gè)例子

shepherd使用4個(gè)字符為一組的加密方式,第一組shep ,第二組heph ,第三組ephe ,第四組pher … 依次類推。

如果需要檢索所有包含檢索條件4個(gè)字符的數(shù)據(jù)比如:pher ,加密字符后通過(guò) key like “%partial%” 查庫(kù)。

分詞加密實(shí)現(xiàn)

public static String splitValueEncrypt(String value, int splitLength) {
       //檢查參數(shù)是否合法
       if (StringUtils.isBlank(value) && splitLength <= 0) {
           return null;
      }
       String encryptValue = "";

       //獲取整個(gè)字符串可以被切割成字符子串的個(gè)數(shù)
       int n = (value.length() - splitLength + 1);
?
       //分詞(規(guī)則:分詞長(zhǎng)度根據(jù)【splitLength】且每次分割的開(kāi)始跟結(jié)束下標(biāo)加一)
       for (int i = 0; i < n; i++) {
           String splitValue = value.substring(i, splitLength++);
           encryptValue += encrypt(splitValue);
      }
?
       return encryptValue;
  }
?
   /**
    * 獲取加密值
    *
    * @param value 加密值
    * @return
    */
   private static String encrypt(String value) {
       // 這里進(jìn)行加密
       return  null;
  }

基于上面分詞加密保存到擴(kuò)展列,同時(shí)要求對(duì)原字段的正刪改查對(duì)需要對(duì)其相應(yīng)的擴(kuò)展列適配,還要注意由于分詞之后導(dǎo)致擴(kuò)展列的長(zhǎng)度可能是原字段幾倍甚至幾十倍,所以務(wù)必在開(kāi)發(fā)之前選擇和合適分詞長(zhǎng)度和加密算法,一旦加密開(kāi)始之后,再更改成本就較高了。像如果手機(jī)號(hào)我們只支持后8位搜索、身份證號(hào)只支持后4位搜索,這樣我們就可以通過(guò)原字段截取后面位數(shù)直接加密存儲(chǔ)到擴(kuò)展列,不需要再分詞。

3.數(shù)據(jù)脫敏

實(shí)際的業(yè)務(wù)開(kāi)發(fā)過(guò)程中,我們經(jīng)常需要對(duì)用戶的隱私數(shù)據(jù)進(jìn)行脫敏處理。所謂脫敏處理其實(shí)就是將數(shù)據(jù)進(jìn)行混淆隱藏,例如用戶手機(jī)信息展示178****5939,以免泄露個(gè)人隱私信息。

3.1實(shí)現(xiàn)思路

思路比較簡(jiǎn)單:在接口返回?cái)?shù)據(jù)之前按要求對(duì)數(shù)據(jù)進(jìn)行脫敏加工之后再返回前端。

一開(kāi)始打算用@ControllerAdvice去實(shí)現(xiàn),但發(fā)現(xiàn)需要自己去反射類獲取注解,當(dāng)返回對(duì)象比較復(fù)雜,需要遞歸去反射,性能一下子就會(huì)降低,于是換種思路,我想到平時(shí)使用的@JsonFormat,跟我現(xiàn)在的場(chǎng)景很類似,通過(guò)自定義注解跟字段解析器,對(duì)字段進(jìn)行自定義解析。

脫敏字段類型枚舉

public enum MaskEnum {
   /**
    * 中文名
    */
   CHINESE_NAME,
   /**
    * 身份證號(hào)
    */
   ID_CARD,
   /**
    * 座機(jī)號(hào)
    */
   FIXED_PHONE,
   /**
    * 手機(jī)號(hào)
    */
   MOBILE_PHONE,
   /**
    * 地址
    */
   ADDRESS,
   /**
    * 電子郵件
    */
   EMAIL,
   /**
    * 銀行卡
    */
   BANK_CARD
}

脫敏注解類:用在脫敏字段之上

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = MaskSerialize.class)
public @interface FieldMask {
?
   /**
    * 脫敏類型
    * @return
    */
   MaskEnum value();
}

脫敏序列化類

public class MaskSerialize extends JsonSerializer<String> implements ContextualSerializer {
?
   /**
    * 脫敏類型
    */
   private MaskEnum type;
?
?
   @Override
   public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
       switch (this.type) {
           case CHINESE_NAME:
          {
               jsonGenerator.writeString(MaskUtils.chineseName(s));
               break;
          }
           case ID_CARD:
          {
               jsonGenerator.writeString(MaskUtils.idCardNum(s));
               break;
          }
           case FIXED_PHONE:
          {
               jsonGenerator.writeString(MaskUtils.fixedPhone(s));
               break;
          }
           case MOBILE_PHONE:
          {
               jsonGenerator.writeString(MaskUtils.mobilePhone(s));
               break;
          }
           case ADDRESS:
          {
               jsonGenerator.writeString(MaskUtils.address(s, 4));
               break;
          }
           case EMAIL:
          {
               jsonGenerator.writeString(MaskUtils.email(s));
               break;
          }
           case BANK_CARD:
          {
               jsonGenerator.writeString(MaskUtils.bankCard(s));
               break;
          }
      }
  }
?
   @Override
   public JsonSerializer <?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMAppingException {
       // 為空直接跳過(guò)
       if (beanProperty == null) {
           return serializerProvider.findNullValueSerializer(beanProperty);
      }
       // 非String類直接跳過(guò)
       if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
           FieldMask fieldMask = beanProperty.getAnnotation(FieldMask.class);
           if (fieldMask == null) {
               fieldMask = beanProperty.getContextAnnotation(FieldMask.class);
          }
           if (fieldMask != null) {
               // 如果能得到注解,就將注解的 value 傳入 MaskSerialize
               return new MaskSerialize(fieldMask.value());
          }
      }
       return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
  }
?
   public MaskSerialize() {}
?
   public MaskSerialize(final MaskEnum type) {
       this.type = type;
  }
} 

3.2使用示例

在發(fā)送短信記錄的接口上對(duì)手機(jī)號(hào)進(jìn)行脫敏:

@FieldMask(MaskEnum.MOBILE_PHONE)
   private String mobile;

調(diào)用接口返回?cái)?shù)據(jù)如下:

{
 "code": 200,
 "msg": "OK",
 "data": {
   "list": [
    {
       "id": 1565599123774607362,
       "signId": 8389008488923136,
       "templateId": 8445337328943104,
       "templateType": 1,
       "content": "可愛(ài)的${name},博客文章已于${submitTime}上傳更新,請(qǐng)抽空瀏覽。",
       "channelType": 0,
       "mobile": "178****5939",
       "sendStatus": 0,
       "receiveStatus": 0
    }
  ],
   "total": 19,
   "pages": 19
}
}

 4.總結(jié)

基于上面內(nèi)容我們總結(jié)如何在數(shù)據(jù)存儲(chǔ)層進(jìn)行數(shù)據(jù)安全加固來(lái)達(dá)到系統(tǒng)的更安全性,可以這么說(shuō)沒(méi)有最安全的系統(tǒng)只有更安全的系統(tǒng)。所以我們?cè)陂_(kāi)發(fā)歷程中都會(huì)窮極一生去加固系統(tǒng)安全性能。當(dāng)然了,加強(qiáng)系統(tǒng)安全性的方式還有很多種,我們最近只是圍繞基于Spring Boot和SpringMVC框架中有效優(yōu)雅地實(shí)現(xiàn)數(shù)據(jù)安全性,感興趣的小伙伴可以自行了解其他加固方式。

分享到:
標(biāo)簽:Spring Boot
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定