更新時(shí)間:2022-11-21 來(lái)源:黑馬程序員 瀏覽量:
概述
存在數(shù)據(jù)庫(kù)中的數(shù)據(jù)對(duì)于普通用戶(hù)而言是不可見(jiàn)的,好像是藏起來(lái)了一樣,但對(duì)于開(kāi)發(fā)者,只要知道數(shù)據(jù)庫(kù)的連接地址、用戶(hù)名、密碼,則數(shù)據(jù)不再安全;這也意味著,一旦連接數(shù)據(jù)庫(kù)的配置文件暴露出去,則數(shù)據(jù)不再安全。
應(yīng)用場(chǎng)景
開(kāi)發(fā)中的數(shù)據(jù)庫(kù)配置文件或配置中心中的配置信息
API介紹
MybatisPlus中有個(gè)針對(duì)配置項(xiàng)加密處理的
代碼實(shí)現(xiàn)
1.創(chuàng)建mp工程
創(chuàng)建maven工程,結(jié)構(gòu)如下:
2.代碼編寫(xiě)
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)類(lèi)
package com.itheima; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @version 1.0 * @description 說(shuō)明 * @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 說(shuō)明 * @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 說(shuō)明 * @package com.itheima.mapper */ @Repository public interface UserMapper extends BaseMapper<User> { }
service接口與實(shí)現(xiàn)類(lèi)
package com.itheima.service; import com.baomidou.mybatisplus.extension.service.IService; import com.itheima.pojo.User; /** * @version 1.0 * @description 說(shuō)明 * @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 說(shuō)明 * @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 說(shuō)明 * @package com.itheima.controller */ @RestController @RequestMapping("user") public class UserController { @Autowired private UserService userService; @GetMapping public List<User> listAll(){ return userService.list(); } }
啟動(dòng)測(cè)試
生成加密后的內(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 說(shuō)明 * @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
添加啟動(dòng)參數(shù)
運(yùn)行效果
3.優(yōu)化
目的
啟動(dòng)時(shí),需要指定密鑰才能使用,如果我們把它封裝到一個(gè)jar里,讓它啟動(dòng)時(shí)自動(dòng)去加載密鑰,且密鑰可配置,那這樣就更靈活了。
分析
通過(guò)查看MybatisPlus加載的源碼,其做解密處理的類(lèi)如下:
/* * 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原理,在類(lèi)所在的jar下的META-INF/spring.factories中配置了這個(gè)SafetyEncryptProcessor。那我們能否也來(lái)定義一個(gè)這樣的配置處理器,判斷環(huán)境配置中是否配置了--mpw.key,如果沒(méi)有配置,則給它配置上,這樣就不用在啟動(dòng)時(shí)添加參數(shù)來(lái)運(yùn)行了。
實(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 說(shuō)明 * @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
```
安裝到本地倉(cāng)庫(kù)
修改mp工程添加依賴(lài)
修改pom.xml,添加mysafe的依賴(lài)
<dependency> <groupId>com.itheima</groupId> <artifactId>mysafe</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
4.測(cè)試結(jié)果
去除啟動(dòng)時(shí)的參數(shù)設(shè)置。再啟動(dòng)后訪(fǎng)問(wèn)頁(yè)面、效果如下:
總結(jié)
1.MybatisPlus利用了springboot的配置信息增強(qiáng)器與SPI機(jī)制來(lái)實(shí)現(xiàn)對(duì)配置文件中敏感數(shù)據(jù)的解密處理。
2.mysafe工程打成的jar包,將來(lái)就上傳到企業(yè)的內(nèi)部服務(wù)器上,當(dāng)密鑰變更時(shí),重新打包即可。
3.而我們將來(lái)布署項(xiàng)目到服務(wù)器上時(shí),也肯定存在配置文件,一旦配置信息暴露,則數(shù)據(jù)庫(kù)就危險(xiǎn)了。通過(guò)加密手段能夠讓破解者增加破解阻礙。
4.此次練習(xí)僅做拋磚引玉作用,關(guān)于加密與解密是沒(méi)有做到那么嚴(yán)謹(jǐn)?shù)?,需要結(jié)合自己公司實(shí)際情況去調(diào)整。