存在數(shù)據(jù)庫中的數(shù)據(jù)對于普通用戶而言是不可見的,好像是藏起來了一樣,但對于開發(fā)者,只要知道數(shù)據(jù)庫的連接地址、用戶名、密碼,則數(shù)據(jù)不再安全;這也意味著,一旦連接數(shù)據(jù)庫的配置文件暴露出去,則數(shù)據(jù)不再安全。
應用場景
開發(fā)中的數(shù)據(jù)庫配置文件或配置中心中的配置信息
API介紹
MybatisPlus中有個針對配置項加密處理的
代碼實現(xiàn)
1.創(chuàng)建mp工程
創(chuàng)建maven工程,結構如下:
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
啟動類
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接口與實現(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();
}
}
啟動測試
生成加密后的內容
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);
}
}
替換配置文件
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
添加啟動參數(shù)
運行效果
3.優(yōu)化
目的
啟動時,需要指定密鑰才能使用,如果我們把它封裝到一個jar里,讓它啟動時自動去加載密鑰,且密鑰可配置,那這樣就更靈活了。
分析
通過查看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;
}
}
/**
* 處理加密內容
*/
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)先級上
if (CollectionUtils.isNotEmpty(map)) {
environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map));
}
}
}
}
其使用了SPI原理,在類所在的jar下的META-INF/spring.factories中配置了這個SafetyEncryptProcessor。那我們能否也來定義一個這樣的配置處理器,判斷環(huán)境配置中是否配置了–mpw.key,如果沒有配置,則給它配置上,這樣就不用在啟動時添加參數(shù)來運行了。
實現(xiàn)
創(chuàng)建配置工程mysafe
代碼清單
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
“`
安裝到本地倉庫
修改mp工程添加依賴
修改pom.xml,添加mysafe的依賴
<dependency>
<groupId>com.itheima</groupId>
<artifactId>mysafe</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.測試結果
去除啟動時的參數(shù)設置。再啟動后訪問頁面、效果如下:
總結
1.MybatisPlus利用了springboot的配置信息增強器與SPI機制來實現(xiàn)對配置文件中敏感數(shù)據(jù)的解密處理。
2.mysafe工程打成的jar包,將來就上傳到企業(yè)的內部服務器上,當密鑰變更時,重新打包即可。
3.而我們將來布署項目到服務器上時,也肯定存在配置文件,一旦配置信息暴露,則數(shù)據(jù)庫就危險了。通過加密手段能夠讓破解者增加破解阻礙。
4.此次練習僅做拋磚引玉作用,關于加密與解密是沒有做到那么嚴謹?shù)?,需要結合自己公司實際情況去調整。