首頁技術(shù)文章正文

Java培訓(xùn):MybatisPlus數(shù)據(jù)安全

更新時(shí)間:2022-11-21 來源:黑馬程序員 瀏覽量:

  概述

  存在數(shù)據(jù)庫中的數(shù)據(jù)對(duì)于普通用戶而言是不可見的,好像是藏起來了一樣,但對(duì)于開發(fā)者,只要知道數(shù)據(jù)庫的連接地址、用戶名、密碼,則數(shù)據(jù)不再安全;這也意味著,一旦連接數(shù)據(jù)庫的配置文件暴露出去,則數(shù)據(jù)不再安全。

  應(yīng)用場景

  開發(fā)中的數(shù)據(jù)庫配置文件或配置中心中的配置信息

  API介紹

  MybatisPlus中有個(gè)針對(duì)配置項(xiàng)加密處理的

 

1669012039360_1.jpg

1669011956303_2.jpg

  代碼實(shí)現(xiàn)

  1.創(chuàng)建mp工程

  創(chuàng)建maven工程,結(jié)構(gòu)如下:

1669012124855_3.jpg

  2.代碼編寫

  pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

  application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
mybatis-plus:
  type-aliases-package: com.itheima.pojo

  啟動(dòng)類

package com.itheima;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima
 */
@SpringBootApplication
@MapperScan(basePackages = "com.itheima.mapper")
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

  pojo

package com.itheima.pojo;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima.pojo
 */
@Data
public class User {
    private Integer id;
    private String username;
    @TableField(select = false)
    private String password;
    private String salt;

}

  mapper

package com.itheima.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.pojo.User;
import org.springframework.stereotype.Repository;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima.mapper
 */
@Repository
public interface UserMapper extends BaseMapper<User> {
}

  service接口與實(shí)現(xiàn)類

package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.pojo.User;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima.service
 */
public interface UserService extends IService<User> {
}
package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima.service.impl
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

  controller

package com.itheima.controller;

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima.controller
 */
@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> listAll(){
        return userService.list();
    }

}

  啟動(dòng)測試

  生成加密后的內(nèi)容

package com.itheima;

import com.baomidou.mybatisplus.core.toolkit.AES;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima
 */
@SpringBootApplication
@MapperScan(basePackages = "com.itheima.mapper")
public class App {
    public static void main(String[] args) {
        String secretKey = AES.generateRandomKey();
        System.out.println("secretKey:" + secretKey);

        String url = AES.encrypt("jdbc:mysql://localhost:3306/springboot?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai", secretKey);
        String username = AES.encrypt("username", secretKey);
        String password = AES.encrypt("password", secretKey);
        System.out.println("url=" +url );
        System.out.println("username=" +username );
        System.out.println("password=" +password );

        SpringApplication.run(App.class,args);
    }
}

1669012363492_4.jpg

  替換配置文件

spring:
  datasource:
    url: mpw:wT9PqZ9Hf4VWgXDuZ/Z1JKfdDyS0sSu3+O2qDkJ/Ulnabpq3z1lZbiThWseQ4DQSx3+SWpufsTysjdYhn6Scsa77AzIIaUgv8DZ17gPxAq88AISmxd9OjxidmY50uBVMkGhP9qAted45zuHBzVrw6Q==
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: mpw:Pnh++mI45YrC4s6JveJYaA==
    password: mpw:Pnh++mI45YrC4s6JveJYaA==
mybatis-plus:
  type-aliases-package: com.itheima.pojo

  添加啟動(dòng)參數(shù)

1669012409610_5.jpg

  運(yùn)行效果

1669012455176_6.jpg

  3.優(yōu)化

  目的

  啟動(dòng)時(shí),需要指定密鑰才能使用,如果我們把它封裝到一個(gè)jar里,讓它啟動(dòng)時(shí)自動(dòng)去加載密鑰,且密鑰可配置,那這樣就更靈活了。

  分析

  通過查看MybatisPlus加載的源碼,其做解密處理的類如下:

/*
 * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.baomidou.mybatisplus.autoconfigure;

import com.baomidou.mybatisplus.core.toolkit.AES;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SimpleCommandLinePropertySource;

import java.util.HashMap;

/**
 * 安全加密處理器
 *
 * @author hubin
 * @since 2020-05-23
 */
public class SafetyEncryptProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        /**
         * 命令行中獲取密鑰
         */
        String mpwKey = null;
        for (PropertySource<?> ps : environment.getPropertySources()) {
            if (ps instanceof SimpleCommandLinePropertySource) {
                SimpleCommandLinePropertySource source = (SimpleCommandLinePropertySource) ps;
                mpwKey = source.getProperty("mpw.key");
                break;
            }
        }
        /**
         * 處理加密內(nèi)容
         */
        if (StringUtils.isNotBlank(mpwKey)) {
            HashMap<String, Object> map = new HashMap<>();
            for (PropertySource<?> ps : environment.getPropertySources()) {
                if (ps instanceof OriginTrackedMapPropertySource) {
                    OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) ps;
                    for (String name : source.getPropertyNames()) {
                        Object value = source.getProperty(name);
                        if (value instanceof String) {
                            String str = (String) value;
                            if (str.startsWith("mpw:")) {
                                map.put(name, AES.decrypt(str.substring(4), mpwKey));
                            }
                        }
                    }
                }
            }
            // 將解密的數(shù)據(jù)放入環(huán)境變量,并處于第一優(yōu)先級(jí)上
            if (CollectionUtils.isNotEmpty(map)) {
                environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map));
            }
        }
    }
}

  其使用了SPI原理,在類所在的jar下的META-INF/spring.factories中配置了這個(gè)SafetyEncryptProcessor。那我們能否也來定義一個(gè)這樣的配置處理器,判斷環(huán)境配置中是否配置了--mpw.key,如果沒有配置,則給它配置上,這樣就不用在啟動(dòng)時(shí)添加參數(shù)來運(yùn)行了。

1669012544198_7.jpg

  實(shí)現(xiàn)

  創(chuàng)建配置工程mysafe

1669012588892_8.jpg

  代碼清單

  pom.xml

<parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.3.8.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

  SafetyEncryptProcessor

package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.Properties;

/**
 * @version 1.0
 * @description 說明
 * @package com.itheima
 */
public class SafetyEncryptProcessor implements EnvironmentPostProcessor, Ordered {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Properties pro = new Properties();
        try {
            pro.load(new ClassPathResource("ert.properties").getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        /**
         * 命令行中獲取密鑰
         */
        String mpwKey = null;
        for (PropertySource<?> ps : environment.getPropertySources()) {
            if (ps instanceof SimpleCommandLinePropertySource) {
                SimpleCommandLinePropertySource source = (SimpleCommandLinePropertySource) ps;
                mpwKey = source.getProperty("mpw.key");
                break;
            }
        }
        if(StringUtils.isEmpty(mpwKey)){
            environment.getPropertySources().addFirst(new SimpleCommandLinePropertySource("mySpringApplicationCommandLineArgs", "--mpw.key=" + pro.getProperty("ert.version")));
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

  spring.factories

  ```.properties

  ert.version=b440fe7fd55dbe26

  org.springframework.boot.env.EnvironmentPostProcessor=\

  com.itheima.SafetyEncryptProcessor

  ```

  ert.properties

  ```properties

  ert.version=2ac6625cb3188f52

  ```

  安裝到本地倉庫

1669012678475_9.jpg

  修改mp工程添加依賴

  修改pom.xml,添加mysafe的依賴

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>mysafe</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

  4.測試結(jié)果

  去除啟動(dòng)時(shí)的參數(shù)設(shè)置。再啟動(dòng)后訪問頁面、效果如下:

1669012734787_10.jpg

  總結(jié)

  1.MybatisPlus利用了springboot的配置信息增強(qiáng)器與SPI機(jī)制來實(shí)現(xiàn)對(duì)配置文件中敏感數(shù)據(jù)的解密處理。

  2.mysafe工程打成的jar包,將來就上傳到企業(yè)的內(nèi)部服務(wù)器上,當(dāng)密鑰變更時(shí),重新打包即可。

  3.而我們將來布署項(xiàng)目到服務(wù)器上時(shí),也肯定存在配置文件,一旦配置信息暴露,則數(shù)據(jù)庫就危險(xiǎn)了。通過加密手段能夠讓破解者增加破解阻礙。

  4.此次練習(xí)僅做拋磚引玉作用,關(guān)于加密與解密是沒有做到那么嚴(yán)謹(jǐn)?shù)?,需要結(jié)合自己公司實(shí)際情況去調(diào)整。

分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!