Jetons un coup d'œil à cette configuration :
spring: # 数据库链接配置 datasource: url: jdbc:mysql://xx.xx.xx.xx:3306/database driver-class-name: com.mysql.cj.jdbc.Driver username: root password: "123456"
La valeur correspondante de notre configuration ci-dessus spring.datasource.password
est 123456
. placé directement C'est très inapproprié dans le fichier de configuration. Ce que nous devons faire est de changer la valeur correspondante en un texte chiffré, comme suit : spring.datasource.password
对应的值为123456
,这么敏感的信息直接放在配置文件中很不合适,我们要做的就是对应的值改成一个加密的密文,如下:
spring: # 数据库链接配置 datasource: url: jdbc:mysql://xx.xx.xx.xx:3306/database driver-class-name: com.mysql.cj.jdbc.Driver username: root password: "AES(DzANBAhBWXxZqAOsagIBCoaw8FV4gYRbid7G70UEM24=)"
这样的话,即使该配置文件被有心之人拿去,也不知道真正的数据库密码是啥,也就无法构成对项目的侵害风险;
我们为了实现这个功能,需要了解Spring
的相关扩展点以及对应的数据加解密知识,我们先来看看我们应该通过Spring
的哪个扩展点进行切入;
我们想要拦截配置数据的话,可以通过实现自定义的BeanFactoryPostProcessor
来处理:
public class PropertySourcePostProcessor implements BeanFactoryPostProcessor { private ConfigurableEnvironment environment; public PropertySourcePostProcessor(ConfigurableEnvironment environment) { this.environment = environment; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 从ConfigurableEnvironment中取出所有的配置数据 MutablePropertySources propertySources = this.environment.getPropertySources(); propertySources.stream() // 过滤不需要包装的对象 .filter(s -> !noWrapPropertySource(s)) // 包装所有的PropertySource .map(s -> new EncryPropertySource(s)) .collect(Collectors.toList()) // 替换掉propertySources中的PropertySource .forEach(wrap -> propertySources.replace(wrap.getName(), wrap)); } private boolean noWrapPropertySource(PropertySource propertySource) { return propertySource instanceof EncryPropertySource || StringUtils.equalsAny(propertySource.getClass().getName(), "org.springframework.core.env.PropertySource$StubPropertySource", "org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"); } }
基本原理解析如下:
1.通过ConfigurableEnvironment
取出所有的PropertySource
并依次遍历;
2.过滤掉不符合我们要求的PropertySource
,因为PropertySource
有很多子类,并不是所有的PropertySource
实例都符合我们包装的要求;
3.对符合要求的PropertySource
做一层包装,其实就是静态代理;
4.用包装好的PropertySource
替换掉之前的PropertySource
实例;
通过上述一系列的操作,我们就可以在PropertySource
取值的时候做一些自定义的操作了,比如针对密文密码进行解密;
剩下的另一个问题就是加解密的问题,密码学里面有对称加密和非对称加密,这两种加密方式的区别就是对称加密的加密解密都需要同一个密钥,而非对称加密加密的时候需要公钥,解密的时候需要私钥;
了解了对称加密与非对称加密的区别,如果我们使用的是对称加密,那么一定要避免密文和密钥放在同一个地方;非对称加密
一定要避免密文和私钥放在同一个地方;
接下来我们要介绍一款专门针对这个需求的jar
工具,它就是jasypt
,我们可以去maven
仓库找到相关的包:
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.5</version> </dependency>
它的实现原理其实就是我们上面所讲述的,通过自定义BeanFactoryPostProcessor
对ConfigurableEnvironment
中的PropertySource
实例进行拦截包装,在包装类的实现上做一层解密操作,这样就实现了对密文密码的解密;
导入上述依赖后,该工具就已经自动生效了,我们就可以修改对应的配置了,首先我们先针对该工具做一些配置:
jasypt: encryptor: # 密钥 password: "" property: # 密文前缀 prefix: "" # 密文后缀 suffix: ""
在上述配置中,jasypt.encryptor.password
是一定要配置的,这就是加解密的密钥,默认的加密算法是PBEWITHHMACSHA512ANDAES_256
;另外jasypt.encryptor.property.prefix
和jasypt.encryptor.property.suffix
分别是密文前缀和密文后缀,是用来标注需要解密的密文的,如果不配置,默认的密文前缀是ENC(
,密文后缀是)
;默认情况下,我们的密文如下所示:
spring: datasource: password: "ENC(DzANBAhBWXxZqAOsagIBCoaw8FV4gYRbid7G70UEM24=)"
还有一个需要注意的点就是jasypt.encryptor.password
不能与密文放在一起,我们可以在项目当中通过系统属性、命令行参数或环境变量传递;
如果jasypt
提供的加解密方式不能满足咱们的项目需求,我们还可以自己实现加解密:
@Bean("jasyptStringEncryptor") public StringEncryptor jasyptStringEncryptor(){ return new StringEncryptor() { @Override public String encrypt(String s) { // TODO 加密 return null; } @Override public String decrypt(String s) { // TODO 解密 return null; } }; }
注意我们的BeanName
,默认情况下一定要设置成jasyptStringEncryptor
,否则不会生效,如果想要改变这个BeanName
,也可以通过修改这个配置参数来自定义StringEncryptor
实例所对应的BeanName
:
jasypt: encryptor: # 自定义StringEncryptor的BeanName bean: ""
生成密文的这个操作还是要自个儿通过调用StringEncryptor
@Component public class StringEncryptorUtil{ @Autowired private StringEncryptor encryptor; public void encrypt(){ String result = encryptor.encrypt("123456"); System.out.println(result); } }Dans ce cas, même si le fichier de configuration est pris par quelqu'un avec des intentions, le Le mot de passe réel de la base de données ne sera pas connu. De quoi s'agit-il, il ne peut pas présenter de risque de violation du projet Analyse des principes🎜🎜Afin d'implémenter cette fonction, nous devons comprendre les points d'extension pertinents de
Spring et les connaissances correspondantes en matière de cryptage et de décryptage des données. Voyons d'abord par quel point d'extension de <code>Spring
nous devons couper 🎜🎜Si nous voulons intercepter les données de configuration, nous pouvons le gérer en implémentant un ; BeanFactoryPostProcessor
personnalisé : 🎜rrreee🎜 Le principe de base est analysé comme suit : 🎜🎜1 Supprimez tous les PropertySource
via ConfigurableEnvironment
et parcourez-les dans l'ordre. 🎜🎜2. Filtrez PropertySource
qui ne répond pas à nos exigences code>, car PropertySource
a de nombreuses sous-classes, toutes les instances de PropertySource
ne répondent pas à nos exigences. exigences d'emballage ; 🎜🎜3. Pour PropertySource Créez une couche d'emballage, qui est en fait un proxy statique 🎜🎜4. Utilisez le <code>PropertySource
empaqueté pour le remplacer ; l'instance PropertySource
précédente ; 🎜🎜Grâce à la série d'opérations ci-dessus, nous pouvons effectuer certaines opérations personnalisées lorsque PropertySource
prend la valeur, comme le déchiffrement du mot de passe chiffré 🎜🎜Le ; L'autre problème restant est la question du cryptage et du déchiffrement. Il existe un cryptage symétrique et un cryptage asymétrique. La différence entre ces deux méthodes de cryptage est que le cryptage symétrique nécessite la même clé pour le cryptage et le déchiffrement, tandis que le cryptage asymétrique nécessite une clé publique pour le cryptage et le déchiffrement. une clé privée pour le décryptage ; 🎜🎜Comprendre Comprendre la différence entre le chiffrement symétrique et le chiffrement asymétrique. Si nous utilisons le chiffrement symétrique, nous devons éviter de placer le texte chiffré et la clé au même endroit ; évitez de placer le texte chiffré et la clé privée au même endroit. La clé est placée au même endroit 🎜🎜Introduction à l'outil🎜🎜Ensuite, nous présenterons un outil jar
spécifiquement pour ce besoin, il s'agit de jasypt, on peut se rendre dans l'entrepôt maven pour trouver le package concerné : 🎜rrreee🎜 Son principe de mise en œuvre est en fait celui que nous avons décrit ci-dessus, en personnalisant BeanFactoryPostProcessor
à ConfigurableEnvironment
L'instance PropertySource
est interceptée et empaquetée, et une couche d'opération de décryptage est effectuée sur l'implémentation de la classe d'empaquetage, réalisant ainsi le déchiffrement du mot de passe chiffré 🎜 ; 🎜Après avoir importé les dépendances ci-dessus, l'outil prendra automatiquement effet, nous pouvons modifier la configuration correspondante. Tout d'abord, nous effectuons quelques configurations pour l'outil : 🎜rrreee🎜Dans la configuration ci-dessus, jasypt.encryptor.password doit être configuré, qui est le cryptage et le déchiffrement La clé, l'algorithme de cryptage par défaut est <code>PBEWITHMACSHA512ANDAES_256
en plus, jasypt.encryptor.property.prefix
et jasypt. encryptor.property.suffix
sont respectivement Le préfixe et le suffixe du texte chiffré sont utilisés pour marquer le texte chiffré qui doit être déchiffré. S'il n'est pas configuré, le préfixe du texte chiffré par défaut est ENC(
, et le suffixe du texte chiffré est )
; Par défaut, notre texte chiffré ressemble à ceci : 🎜rrreee🎜 Un autre point à noter est que jasypt.encryptor.password
ne peut pas être placé ensemble. avec le texte chiffré. Nous pouvons transmettre les propriétés du système, les paramètres de ligne de commande ou les variables d'environnement ; notre projet a besoin, nous pouvons également implémenter le cryptage et le déchiffrement par vous-même : 🎜rrreee🎜Faites attention à notre BeanName
Il doit être défini sur jasyptStringEncryptor
par défaut, sinon il ne le sera pas. Si vous souhaitez modifier ce BeanName
, vous pouvez également personnaliser le BeanName
correspondant à l'instance StringEncryptor
en modifiant ce paramètre de configuration : 🎜 rrreeeStringEncryptor
. Vous pouvez vous référer au code suivant : 🎜rrreee🎜After. Dans l'ensemble, l'opération qui nécessite un chiffrement ne doit être effectuée qu'une seule fois dans le cycle de vie du projet, il suffit donc d'écrire une classe d'outils et de l'appeler. 🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!