jasypt-spring-boot-starter實現(xiàn)加解密和數(shù)據(jù)返顯方式
jasypt-spring-boot-starter實現(xiàn)加解密和數(shù)據(jù)返顯
一、青銅:jasypt-spring-boot-starter在springboot中的加解密(默認(rèn)加密法)
1、導(dǎo)包
<!--實現(xiàn)自動 加密解密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>注意這里引入的版本號,2.x和3.x會有差別,后面會講
2、配置yml
- 2.1、關(guān)于jasypt部分的配置:
jasypt:
encryptor:
#加解密的密碼
password: atpingan
#jasypt默認(rèn)更改了算法,如果不指定那么會報錯:failed to bind properties under 'spring.datasource.druid.password' to java.lang.String
#解決辦法:①把版本降到2.x②指定加密方法,如下
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator- 2.2、關(guān)于加密部分的配置:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.200.141:3306/mysql?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
#加密密碼atpingan,ENC() 是默認(rèn)加解密的標(biāo)識
username: ENC(kud5ZnaMJYve284geT0ITw==)
password: ENC(0CwfH4246HP22Rv74d/ZPw==)其中ENC()是默認(rèn)加密法的固定配置,后面會講自定義加密法
- 2.3、重點:(2.x和3.x的區(qū)別)
jasypt默認(rèn)更改了算法,如果不指定那么會報錯:
failed to bind properties under spring.datasource.druid.password' to java.lang.String
#解決辦法:
- ①把版本降到2.x
- ②指定加密方法,如下
jasypt:
encryptor:
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
password: atpingan3、計算ENC()中的內(nèi)容
上面你是否疑惑ENC(kud5ZnaMJYve284geT0ITw==)中的內(nèi)容是怎么來的?
他是根據(jù):
jasypt:
encryptor:
#加解密的密碼
password: atpingan的密碼來計算出來的。自己建一個main方法類,計算出來即可,計算完這個類就沒用了,可以刪除。
public static void main(String[] args) {
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword("atpingan");
String userName = textEncryptor.encrypt("root");
String passWord = textEncryptor.encrypt("123456");
System.out.println("userName==="+userName);
System.out.println("passWord==="+passWord);
}4、添加注解
在啟動類上添加注解 @EnableEncryptableProperties
啟動項目,發(fā)現(xiàn)正常,那就是對數(shù)據(jù)庫的賬號密碼實現(xiàn)了加密
二、黃金:自定義加密、解密及前綴后綴方法
1、導(dǎo)包(只列出最主要的包,其他相關(guān)的包不一一列舉)
<!--實現(xiàn)自動 加密解密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>2、寫配置類(重點)
springboot中的條件注解匯總
| 注解 | 作用 |
|---|---|
| @Conditional | 判斷是否滿足當(dāng)前指定條件 |
| @ConditionalOnJava | 系統(tǒng)的java版本是否符合要求 |
| @ConditionalOnBean | 容器中存在指定Bean才執(zhí)行 |
| @ConditionalOnMissingBean | 容器中不存在指定Bean才執(zhí)行 |
| @ConditionalOnExpression | 滿足SpEL表達式指定 |
| @ConditionalOnClass | 系統(tǒng)中有指定的類 |
| @ConditionalOnMissingClass | 系統(tǒng)中沒有指定的類 |
| @ConditionalOnSingleCandidate | 容器中只有一個指定的Bean,或者這個Bean是首選Bean |
| @ConditionalOnProperty | 系統(tǒng)中指定的屬性是否有指定的值 |
| @ConditionalOnResource | 類路徑下是否存在指定資源文件 |
| @ConditionalOnWebApplication | 當(dāng)前是web環(huán)境 |
| @ConditionalOnNotWebApplication | 當(dāng)前不是web環(huán)境 |
| @ConditionalOnJndi | JNDI存在指定項 |
2.1、啟動加載的配置類JasyptConfiguration ,即入口。
@Configuration
public class JasyptConfiguration {
//這里的名字必須是jasyptStringEncryptor,不能改動
@Bean(name = "jasyptStringEncryptor")
@ConditionalOnMissingBean
public StringEncryptor stringEncryptor(MyEncryptablePropertyDetector propertyDetector){
return new DefaultEncryptor(propertyDetector);
}
//這里的名字必須是encryptablePropertyDetector,不能改動
@Bean(name = "encryptablePropertyDetector")
@ConditionalOnMissingBean
public MyEncryptablePropertyDetector encryptablePropertyDetector() {
return new MyEncryptablePropertyDetector();
}
}2.2、監(jiān)聽類,它會找到配置文件中包含指定前后綴的數(shù)據(jù),如這里指定的 ikms( 和 )
public class MyEncryptablePropertyDetector implements EncryptablePropertyDetector {
private String prefix = "ikms(";
private String suffix = ")";
public MyEncryptablePropertyDetector() {
}
public MyEncryptablePropertyDetector(String prefix, String suffix) {
Assert.notNull(prefix, "Prefix can't be Null");
Assert.notNull(suffix, "Suffix can't be Null");
this.prefix = prefix;
this.suffix = suffix;
}
/**
*判斷配置文件中的數(shù)據(jù)是否是按這里指定前后綴組裝的
**/
@Override
public boolean isEncrypted(String message) {
if (StringUtils.isBlank(message)) {
return false;
} else {
String trimmedValue = message.trim();
return trimmedValue.startsWith(this.prefix) && trimmedValue.endsWith(this.suffix);
}
}
@Override
public String unwrapEncryptedValue(String message) {
/**
*獲取到 上面方法返回true的數(shù)據(jù)
* 此處原數(shù)據(jù)返回,不作處理,統(tǒng)一在DefaultEncryptor處理
*/
return message;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}2.3、實現(xiàn)凱撒解密并把數(shù)據(jù)返顯回配置文件
public class DefaultEncryptor implements StringEncryptor {
/**
*獲取寫在配置文件中的參數(shù),這里是解密的密碼
**/
@Value("${jasypt.encryptor.password}")
private int decryptPassword;
private MyEncryptablePropertyDetector propertyDetector;
public DefaultEncryptor() {
}
public DefaultEncryptor( MyEncryptablePropertyDetector propertyDetector) {
this.propertyDetector = propertyDetector;
}
/**
*這里是加密方法,我們不在這里加密,原參數(shù)返回
**/
@Override
public String encrypt(String encryptMessage) {
return encryptMessage ;
}
/**
*凱撒解密
**/
@Override
public String decrypt(String decryptMessage) {
/**
* 從MyEncryptablePropertyDetector的 unwrapEncryptedValue方法返回的數(shù)據(jù)在這里處理
*/
String prefix = propertyDetector.getPrefix();
String suffix = propertyDetector.getSuffix();
int prefixIndex = decryptMessage.indexOf(prefix);
int suffixIndex = decryptMessage.indexOf(suffix);
/**
* 截取括號中間部分,例如:ikms(鄰居小玲) 里面的:鄰居小玲
*/
decryptMessage = decryptMessage.substring(prefixIndex+prefix.length(),suffixIndex);
/**
* 做凱撒解密:加解密公共方法,請往后看
*/
String result = KaiserUtil.decryptKaiser(decryptMessage,decryptPassword);
return result;
}
}2.4、凱撒加解密的公共方法
public class KaiserUtil {
public static void main(String[] args) {
String encryptKaiser = encryptKaiser("root",123456789);
String decryptKaiser = decryptKaiser(encryptKaiser, 123456789);
System.out.println("encryptKaiser==="+encryptKaiser);
System.out.println("decryptKaiser==="+decryptKaiser);
}
/**
* 使用凱撒加密方式加密數(shù)據(jù)
* @param orignal :原文
* @param key :密鑰
* @return :加密后的數(shù)據(jù)
*/
public static String encryptKaiser(String orignal, int key) {
// 將字符串轉(zhuǎn)為字符數(shù)組
char[] chars = orignal.toCharArray();
StringBuilder sb = new StringBuilder();
// 遍歷數(shù)組
for (char aChar : chars) {
// 獲取字符的ASCII編碼
int asciiCode = aChar;
// 偏移數(shù)據(jù)
asciiCode += key;
// 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
char result = (char) asciiCode;
// 拼接數(shù)據(jù)
sb.append(result);
}
return sb.toString();
}
/**
* 使用凱撒加密方式解密數(shù)據(jù)
* @param encryptedData :密文
* @param key :密鑰
* @return : 源數(shù)據(jù)
*/
public static String decryptKaiser(String encryptedData, int key) {
// 將字符串轉(zhuǎn)為字符數(shù)組
char[] chars = encryptedData.toCharArray();
StringBuilder sb = new StringBuilder();
// 遍歷數(shù)組
for (char aChar : chars) {
// 獲取字符的ASCII編碼
int asciiCode = aChar;
// 偏移數(shù)據(jù)
asciiCode -= key;
// 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
char result = (char) asciiCode;
// 拼接數(shù)據(jù)
sb.append(result);
}
return sb.toString();
}
}3、寫配置文件
3.1、配置jasypt密鑰及指定加解密方法
jasypt:
encryptor:
#解密的密鑰
password: 123456789
#jasypt 3.x版本默認(rèn)更改了算法,如果不指定那么會報錯:failed to bind properties under 'spring.datasource.druid.password' to java.lang.String
#解決辦法:①把版本降到2.x ②指定加密方法,如下
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator注意:
- 這里的
algorithm和iv-generator-classname可以不要,因為我們重寫了解密方法,不用它本身的加解密方法。 - 寫上也不會報錯,用不上。我留下它們主要是想說明版本差異造成的報錯及解決辦法
3.2、數(shù)據(jù)庫的連接配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.200.141:3306/mysql?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
#加密密碼atpingan
#username: root
#password: 420188
username: ikms(????)
password: ikms(??????)至此就寫完了自定義jasypt的加解密,這種方法也不需要加@EnableEncryptableProperties,因為加載的是我們自定義加解密方法
三、王者:使用jasypt做一個starter
最近遇到一個業(yè)務(wù)需求,要把數(shù)據(jù)庫、redis、es等等的密碼都從kms的密碼保管箱中獲取,場景是項目啟動就獲取并加載到配置文件中,java代碼只需通過@Value去取,實現(xiàn)方法是自定義一個starter,并使用jasypt,不需要它的加解密功能,只要返顯數(shù)據(jù)到配置文件即可。
我的文件目錄結(jié)構(gòu)是:

1、定義starter的啟動加載文件spring.factories
下載resource/META-INF下面:(它是starter的入口)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.pingan.idaas.datadecryptbootautoconfigure.jasypt.JasyptConfiguration
2、寫jasypt的文件
2.1、配置類,spring.factories加載的就是這個類
@Configuration
@EnableConfigurationProperties(DataDeCryptProperties.class)
public class JasyptConfiguration {
@Bean
@ConditionalOnMissingBean(DataDecryptService.class)
public DataDecryptService dataDecryptService(DataDeCryptProperties dataDeCryptProperties){
return new DataDecryptService(dataDeCryptProperties);
}
@Bean(name="encryptablePropertyDetector")
@ConditionalOnMissingBean
public MyEncryptablePropertyDetector encryptablePropertyDetector(){
return new MyEncryptablePropertyDetector();
}
// 注意這里,因為它有多個實現(xiàn)類,其他實現(xiàn)類實例化后有可能導(dǎo)致,此處不實例化,所以這里要用@Primary標(biāo)注
@Bean(name = "jasyptStringEncryptor")
@Primary
public StringEncryptor stringEncryptor(DataDecryptService dataDecryptService,MyEncryptablePropertyDetector myEncryptablePropertyDetector){
return new DefaultEncryptor(dataDecryptService,myEncryptablePropertyDetector);
}
}2.2、獲取配置文件參數(shù)并判斷出指定前后綴的是哪些
public class MyEncryptablePropertyDetector implements EncryptablePropertyDetector {
private String prefix = "AIDSREN(";
private String suffix = ")";
public MyEncryptablePropertyDetector() {
}
public MyEncryptablePropertyDetector(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
@Override
public boolean isEncrypted(String message) {
if(StringUtils.isBlank(message)){
return false;
}
String trimValue = message.trim();
return trimValue.startsWith(this.prefix) && trimValue.endsWith(this.suffix);
}
@Override
public String unwrapEncryptedValue(String message) {
return message;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}2.3、這里本來是加解密的類,但是我們根據(jù)業(yè)務(wù)需求,這里不做加解密,做業(yè)務(wù)邏輯,并返顯數(shù)據(jù)到配置文件參數(shù)中
public class DefaultEncryptor implements StringEncryptor {
private static final Log log = LogFactory.getLog(DefaultEncryptor.class);
private DataDecryptService dataDecryptService;
private MyEncryptablePropertyDetector propertyDetector;
@Autowired
private DataDeCryptProperties dataDeCryptProperties;
private String innerPrefix = "AEN(";
private String innerSuffix = ")";
public static final String COLON = "::";
public DefaultEncryptor() {
}
public DefaultEncryptor(DataDecryptService dataDecryptService, MyEncryptablePropertyDetector propertyDetector) {
this.dataDecryptService = dataDecryptService;
this.propertyDetector = propertyDetector;
}
public DefaultEncryptor(MyEncryptablePropertyDetector propertyDetector) {
this.propertyDetector = propertyDetector;
}
@Override
public String encrypt(String message) {
return message;
}
/**
* 解密
* @param message
* @return
*/
@Override
public String decrypt(String message) {
String prefix = propertyDetector.getPrefix();
String suffix = propertyDetector.getSuffix();
message = CommonTool.getMessage(message, prefix, suffix);
String result = "";
if (StringUtils.isNotBlank(message)) {
result = getDecryptOrRemoteKey(message);
}
return result;
}
/**
* 獲取含有 :: 的個數(shù)并分支
* @param message
* @return
*/
private String getDecryptOrRemoteKey(String message) {
int countOfColon = countColon(message);
if (countOfColon <= 0) {
return getResultWithNoColon(message);
}
if (countOfColon == 1) {
return getResultWithColon(message);
}
return "";
}
/**
* 未含有 :: 的處理邏輯
* @param message
* @return
*/
private String getResultWithNoColon(String message) {
String trimValue = message.trim();
boolean flag = trimValue.startsWith(innerPrefix) && trimValue.endsWith(innerSuffix);
if(flag){
int decryptPassword = dataDeCryptProperties.getDecryptPassword();
message = CommonTool.getMessage(message, innerPrefix, innerSuffix);
return IdaasKaiserUtil.decryptKaiser(message,decryptPassword);
}
return message;
}
/**
* 含有一個 :: 的處理邏輯
* @param message
* @return
*/
private String getResultWithColon(String message) {
String result = null;
String[] messageArray = message.split(COLON);
String key = messageArray[0];
String value = messageArray[1];
String url = dataDeCryptProperties.getUrl();
result = getByRemoteOrKms(key, url);
if (StringUtils.isBlank(result)) {
result = getResultWithNoColon(value);
}
return result;
}
/**
* 遠程獲取密鑰
* @param key
* @param url
* @return
*/
private String getByRemoteOrKms(String key, String url) {
CustomGetCryptorService cryptorService = getCryptorInstance();
try {
return cryptorService.getRemoteCryptor(url, key);
} catch (Exception e) {
log.error("獲取遠程密鑰失敗", e);
}
return null;
}
/**
* 獲取對象(starter本身密鑰獲取對象 還是 客戶端自定義密鑰獲取對象)
* @return
*/
private CustomGetCryptorService getCryptorInstance() {
String customGetCryptorClass = dataDeCryptProperties.getCustomGetCryptorClass();
CustomGetCryptorService customGetCryptorService = null;
try {
if (StringUtils.isNotBlank(customGetCryptorClass)) {
Class<?> clazz = Class.forName(customGetCryptorClass);
customGetCryptorService = (CustomGetCryptorService) clazz.newInstance();
} else {
customGetCryptorService = new CustomGetCryptorServiceImpl();
}
} catch (Exception e) {
log.error("獲取客戶端對象失??!");
}
return customGetCryptorService;
}
/**
* 計算::的個數(shù)
* @param message
* @return
*/
private int countColon(String message) {
String trimValue = message.trim();
int count = 0;
int length = message.length();
while (trimValue.indexOf(COLON) != -1) {
trimValue = trimValue.substring(trimValue.indexOf(COLON) + 1, length);
count++;
}
return count;
}
}3、寫屬性類和業(yè)務(wù)類
3.1、屬性類HelloProperties即是獲取調(diào)用本starter的yml(或properties)中的指定前綴的數(shù)據(jù)內(nèi)容
這里指定要獲取前綴為atguigu.hello開頭的參數(shù)
@ConfigurationProperties(prefix = "idaas.decerypt")
public class DataDeCryptProperties {
private String url;
private int decryptPassword;
private String customGetCryptorClass;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getDecryptPassword() {
return decryptPassword;
}
public void setDecryptPassword(int decryptPassword) {
this.decryptPassword = decryptPassword;
}
public String getCustomGetCryptorClass() {
return customGetCryptorClass;
}
public void setCustomGetCryptorClass(String customGetCryptorClass) {
this.customGetCryptorClass = customGetCryptorClass;
}
}
3.2、獲取業(yè)務(wù)類
此處僅僅是擺設(shè)未實際用到
public class DataDecryptService {
private DataDeCryptProperties dataDeCryptProperties;
public DataDecryptService() {
}
public DataDecryptService(DataDeCryptProperties dataDeCryptProperties) {
this.dataDeCryptProperties = dataDeCryptProperties;
}
}遠程獲取密鑰口子:
接口:
public interface CustomGetCryptorService {
String getRemoteCryptor(String url, String key);
}實現(xiàn)類:
public class CustomGetCryptorServiceImpl implements CustomGetCryptorService {
private static final Log log = LogFactory.getLog(CustomGetCryptorServiceImpl.class);
@Override
public String getRemoteCryptor(String url, String key) {
// todo 自定義遠程獲取密鑰
log.info("starter獲取遠程密鑰失敗");
return null;
}
}3.3 工具類
public class CommonTool {
public static String getMessage(String message,String prefix,String suffix){
int prefixIndex = message.indexOf(prefix);
int suffixIndex = message.lastIndexOf(suffix);
return message.substring(prefixIndex+prefix.length(),suffixIndex);
}
}public class IdaasKaiserUtil {
/**
* 使用凱撒加密方式加密數(shù)據(jù)
* @param orignal :原文
* @param key :密鑰
* @return :加密后的數(shù)據(jù)
*/
public static String encryptKaiser(String orignal, int key) {
// 將字符串轉(zhuǎn)為字符數(shù)組
char[] chars = orignal.toCharArray();
StringBuilder sb = new StringBuilder();
// 遍歷數(shù)組
for (char aChar : chars) {
// 獲取字符的ASCII編碼
int asciiCode = aChar;
// 偏移數(shù)據(jù)
asciiCode += key;
// 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
char result = (char) asciiCode;
// 拼接數(shù)據(jù)
sb.append(result);
}
return sb.toString();
}
/**
* 使用凱撒加密方式解密數(shù)據(jù)
* @param encryptedData :密文
* @param key :密鑰
* @return : 源數(shù)據(jù)
*/
public static String decryptKaiser(String encryptedData, int key) {
// 將字符串轉(zhuǎn)為字符數(shù)組
char[] chars = encryptedData.toCharArray();
StringBuilder sb = new StringBuilder();
// 遍歷數(shù)組
for (char aChar : chars) {
// 獲取字符的ASCII編碼
int asciiCode = aChar;
// 偏移數(shù)據(jù)
asciiCode -= key;
// 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
char result = (char) asciiCode;
// 拼接數(shù)據(jù)
sb.append(result);
}
return sb.toString();
}
}以上是自定義starter的自動配置類的寫法,這里列出了主要內(nèi)容,忽略了引包,和場景啟動器的寫法,如需細化請參看前一博客關(guān)于自定義starter的寫法。
4、調(diào)用方的配置(調(diào)用方即誰引用我的這個starter)
4.1、引入starter
<dependency>
<groupId>com.pingan.idaas</groupId>
<artifactId>data-decrypt-boot-starter</artifactId>
</dependency>注意:這里的要返顯的字段不要寫在要傳入的字段中,即這里的message1……7不要寫在 atguigu.hello下面.
4.2、配置yml
idaas:
decerypt:
decryptPassword: 12345678 # 最低配置,必須
url: wwww.baidu.com #starter去遠程獲取密鑰的地址(根據(jù)實際配置,若沒有去遠程獲取可不配),非必須
customGetCryptorClass: com.athaite.idaas.CustomGetCryptor #客戶端自己代碼去遠程獲取密鑰的類(此類要實現(xiàn)我們starter的接口CustomGetCryptorService),非必須剩下的就是自己加密的部分了:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: IDSREN(IEN(憸憲憰憱憈憻懇懁憿憺憈慽慽慿憇憀慼慿憄憆慼慿憆憁慼慿憀憇憈憁憁慾憄慽憵懃憺憷憻憯憺憺憭憯憲憻憷憼?wèi)x懃懁憳憡憡憚憋憴憯憺懁憳慴憯憺憺憽懅憛懃憺懂憷憟懃憳懀憷憳懁憋懂懀懃憳慴懃懁憳憣憼憷憱憽憲憳憋懂懀懃憳慴憱憶憯懀憯憱懂憳懀憓憼?wèi)枒椼缿爲(wèi)毐飸寫嬨緫g憆慴憯懃懂憽憠憳憱憽憼?wèi)爲(wèi)槕柖锒畱?)
#url: jdbc:mysql://192.168.183.129:3306/gulimall_admin?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
#username: root
username: AIDSREN(AEN(懀憽憽懂))
#password: 420188
password: AIDSREN(AEN(憂憀慾慿憆憆))特別提醒:
本starter兼容本地解密,starter遠程解密,客戶端自己遠程解密
示例(4種類型):
- 本地解密 :AIDSREN(AEN(懀憽憽懂))
- 本地不解密 :AIDSREN(root)
- 遠程解密(雙冒號后是遠程獲取失敗采用本地密文密鑰):AIDSREN(name::AEN(懀憽憽懂))
- 遠程解密(雙冒號后是遠程獲取失敗采用本地明文密鑰):AIDSREN(name::root)
其他配置項與雙冒號(::)錯開,避免誤判
自此,starter的自定義獲取配置文件傳參、執(zhí)行業(yè)務(wù)方法、獲取返回數(shù)據(jù)就寫完了。優(yōu)點是對代碼無侵入,可插拔,可自定義前后綴加解密方法,留下了遠程獲取的口子,缺點是只有啟動時加載一次,不能動態(tài)感知變化。
四、jasypt原理分析
①META-INF/spring.factories 指定的入口類JasyptSpringCloudBootstrapConfiguration

②在這個類中通過@Import引入 EnableEncryptablePropertiesConfiguration

③在EnableEncryptablePropertiesConfiguration中加載了一個叫EnableEncryptablePropertiesBeanFactoryPostProcessor

④EnableEncryptablePropertiesBeanFactoryPostProcessor實現(xiàn)了一個接口BeanFactoryPostProcessor,Ordered重寫了一個方法 叫postProcessBeanFactory
BeanFactoryPostProcessor:Bean工廠后處理器,在BeanDefinitionMap填充完畢,Bean實例化之前執(zhí)行BeanPostProcessor:Bean后處理器,一般在Bean實例化之后,填充到單例池singletonObjects之前執(zhí)行。初始化前后分別執(zhí)行postProcessBeforeInitialization和postProcessAfterInitialization

該方法中的主要邏輯就是,獲取所有的propertySources。
獲取所有配置文件:MutablePropertySources propSources = environment.getPropertySources();
⑤判斷配置文件中的屬性是否是加密過(是否有指定前綴后綴)

⑥解密

五、springboot加載外部starter的原理(自動配置原理)
springboot加載外部starter的原理:
①springboot的啟動類有一個注解叫@SpringBootApplication

②該注解是個復(fù)合注解,其中有一個@EnableAutoConfiguration
③@EnableAutoConfiguration 也是復(fù)合注解 其中就有 @Import(AutoConfigurationImportSelector.class)

④AutoConfigurationImportSelector類中有getCandidateConfigurations方法

⑤getCandidateConfigurations 方法的loadSpringFactories會去讀配置文件META-INF/spring.factories文件

⑥該文件的org.springframework.boot.autoconfigure.EnableAutoConfiguration指定的類會被加載到

spring容器,這就完成了springboot加載外部bean或者第三方j(luò)ar中的bean
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文詳解SpringBoot如何優(yōu)雅地實現(xiàn)異步調(diào)用
SpringBoot想必大家都用過,但是大家平時使用發(fā)布的接口大都是同步的,那么你知道如何優(yōu)雅的實現(xiàn)異步呢?這篇文章就來和大家詳細聊聊2023-03-03
深入分析:用1K內(nèi)存實現(xiàn)高效I/O的RandomAccessFile類的詳解
本篇文章是對用1K內(nèi)存實現(xiàn)高效I/O的RandomAccessFile類的詳細分析介紹,需要的朋友參考下2013-05-05
SpringSecurity+Redis+Jwt實現(xiàn)用戶認(rèn)證授權(quán)
SpringSecurity是一個強大且靈活的身份驗證和訪問控制框架,本文主要介紹了SpringSecurity+Redis+Jwt實現(xiàn)用戶認(rèn)證授權(quán),具有一定的參考價值,感興趣的可以了解一下2024-07-07
Mybatis實現(xiàn)自定義的typehandler三步曲
這篇文章主要介紹了Mybatis實現(xiàn)自定義的typehandler三步曲的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07
SpringBoot參數(shù)校驗之@Validated的使用詳解
這篇文章主要通過示例為大家詳細介紹一下介紹了SpringBoot參數(shù)校驗中@Validated的使用方法,文中的示例代碼講解詳細,需要的可以參考一下2022-06-06

