欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity中PasswordEncoder的使用

 更新時(shí)間:2025年01月20日 10:23:36   作者:johnny233  
密碼存儲(chǔ)和加密是非常重要的,本文主要介紹了SpringSecurity中PasswordEncoder的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

概述

任何一個(gè)登錄系統(tǒng)的密碼不能明文存儲(chǔ),萬(wàn)一發(fā)生數(shù)據(jù)庫(kù)泄漏事故(不管是內(nèi)部人員導(dǎo)出數(shù)據(jù)庫(kù)數(shù)據(jù)還是被黑客攻擊破解數(shù)據(jù)庫(kù)實(shí)例節(jié)點(diǎn)拿到數(shù)據(jù)庫(kù)數(shù)據(jù)等,又或者是其他情況造成的),將產(chǎn)生巨大的損失。因此明文密碼在存儲(chǔ)到數(shù)據(jù)庫(kù)之前需要加密處理。

加密算法有很多,大致有如下分類:

  • 哈希函數(shù)算法:包括消息摘要算法(MD4,MD5等),消息摘要算法是一種特殊類型的哈希函數(shù)算法,用于將任意長(zhǎng)度的數(shù)據(jù)映射為固定長(zhǎng)度的哈希值或摘要。摘要值通常用于驗(yàn)證數(shù)據(jù)的完整性、數(shù)字簽名、身份驗(yàn)證等用途。算法包括:
    • MD5(Message Digest Algorithm 5):已經(jīng)不推薦使用,存在碰撞攻擊漏洞
    • SHA-1(Secure Hash Algorithm 1):也存在碰撞攻擊漏洞,逐漸被淘汰
    • SHA-256、SHA-384、SHA-512:SHA-2系列,目前被廣泛應(yīng)用,提供更高的安全性
  • 對(duì)稱加密算法使用相同的密鑰進(jìn)行加密和解密。常見(jiàn)的對(duì)稱加密算法包括:
    • DES(Data Encryption Standard):已經(jīng)不推薦使用,因?yàn)槊荑€長(zhǎng)度較短易受到攻擊
    • 3DES(Triple DES):DES增強(qiáng)版,使用三個(gè)密鑰提高安全性
    • AES(Advanced Encryption Standard):目前廣泛應(yīng)用的對(duì)稱加密算法,具有較高的安全性和性能
  • 非對(duì)稱加密算法(公鑰加密算法):
    非對(duì)稱加密算法使用一對(duì)密鑰,分別是公鑰和私鑰,公鑰用于加密,私鑰用于解密。常見(jiàn)的非對(duì)稱加密算法包括:
  • RSA(Rivest-Shamir-Adleman):基于大數(shù)分解難題,被廣泛用于數(shù)字簽名和密鑰交換
  • ECC(Elliptic Curve Cryptography):利用橢圓曲線上的離散對(duì)數(shù)問(wèn)題,相比RSA,提供相同安全級(jí)別下更短的密鑰長(zhǎng)度和更高的性能

反查表、彩虹表

上文提到一些已經(jīng)不推薦使用、逐漸被淘汰的算法,如MD5、SHA-1。因?yàn)椴还苁荕D5還是SHA-1算法,對(duì)于給定的某個(gè)字符串(密碼),經(jīng)過(guò)哈希函數(shù)計(jì)算之后得到的結(jié)果都是固定的。比如admin經(jīng)過(guò)MD5計(jì)算(有16位和32位之分,這里用的是16位)結(jié)果始終是7a57a5a743894a0e,root經(jīng)過(guò)SHA-1計(jì)算后結(jié)果始終是dc76e9f0c0006e8f919e0c515c66dbba3982f785。

那黑客們就可以維護(hù)一個(gè)數(shù)據(jù)庫(kù),其字段包括加密后的密文、加密算法、明文密碼,意味著可以根據(jù)密文反查明文密碼。這就是反查表。

基于反查表,黑客們后來(lái)發(fā)明更高級(jí)的彩虹表。

在Java Web開(kāi)發(fā)中,我們會(huì)遇到各種各樣的安全問(wèn)題。作為最基本的,數(shù)據(jù)庫(kù)密碼的安全性如何得到保證呢?此時(shí)Spring Security隆重登場(chǎng),可以幫助我們解決這個(gè)問(wèn)題。

Spring Security

實(shí)例

加密密碼的配置類(代碼片段):

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity()
public class WebSecurityConfig implements SecurityFilterChain {
	@Resource
	private UserDetailsService userDetailsService;
	
	@Autowired
	public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
		authenticationManagerBuilder
			.userDetailsService(this.userDetailsService)
			.passwordEncoder(passwordEncoder());
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

PasswordEncoder

PasswordEncoder接口定義如下:

public interface PasswordEncoder {
	// 用來(lái)對(duì)明文密碼進(jìn)行加密
	String encode(CharSequence rawPassword);
	// 用來(lái)進(jìn)行密碼比對(duì)
	boolean matches(CharSequence rawPassword, String encodedPassword);
	// 用來(lái)判斷當(dāng)前密碼是否需要升級(jí),默認(rèn)返回false表示不需要升級(jí)
	default boolean upgradeEncoding(String encodedPassword) {
		return false;
	}
}

尚未廢棄的實(shí)現(xiàn)類,都是自適應(yīng)單向函數(shù)(Adaptive One-way Functions)來(lái)處理密碼問(wèn)題,這種函數(shù)在進(jìn)行密碼匹配時(shí),會(huì)有意占用大量系統(tǒng)資源(例如CPU、內(nèi)存等),可以增加惡意用戶攻擊系統(tǒng)的難度。包括:bcrypt、PBKDF2、scrypt以及argon2。

因此實(shí)現(xiàn)類包括:

  • BCryptPasswordEncoder:使用bcrypt強(qiáng)散列算法對(duì)密碼進(jìn)行加密,為提高密碼的安全性,bcrypt算法故意降低運(yùn)行速度,以增強(qiáng)密碼破解的難度。BCryptPasswordEncoder自帶salt加鹽機(jī)制,即使相同的明文每次生成的加密字符串都不相同。默認(rèn)強(qiáng)度為10(參考源碼里的strength字段),開(kāi)發(fā)者可以根據(jù)自己的服務(wù)器性能進(jìn)行調(diào)整,以確保密碼驗(yàn)證時(shí)間約為1秒鐘(官方建議密碼驗(yàn)證時(shí)間為1秒鐘,既可以提高系統(tǒng)安全性,又不會(huì)過(guò)多影響系統(tǒng)運(yùn)行性能)
  • Argon2PasswordEncoder:使用Argon2算法對(duì)密碼進(jìn)行加密,Argon2曾在Password Hashing Competition競(jìng)賽中獲勝。為了解決在定制硬件上密碼容易被破解的問(wèn)題,Argon2也是故意降低運(yùn)算速度,同時(shí)需要大量?jī)?nèi)存
  • Pbkdf2PasswordEncoder:使用PBKDF2算法對(duì)密碼進(jìn)行加密,可用于FIPS(Federal Information Processing Standard,美國(guó)聯(lián)邦信息處理標(biāo)準(zhǔn))認(rèn)證
  • SCryptPasswordEncoder:使用scrypt算法對(duì)密碼進(jìn)行加密

幾個(gè)已經(jīng)被廢棄的基于消息摘要算法的實(shí)現(xiàn)類:

  • NoOpPasswordEncoder:密碼明文存儲(chǔ),不可用于生產(chǎn)環(huán)境
  • Md4PasswordEncoder:使用Md4算法加密密碼
  • LdapShaPasswordEncoder:使用SHA算法
  • StandardPasswordEncoder:使用SHA-256算法
  • MessageDigestPasswordEncoder:使用MD5算法

BCryptPasswordEncoder

public String encode(CharSequence rawPassword) {
	if (rawPassword == null) {
		throw new IllegalArgumentException("rawPassword cannot be null");
	}
	String salt = getSalt();
	return BCrypt.hashpw(rawPassword.toString(), salt);
}

private String getSalt() {
	if (this.random != null) {
		return BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
	}
	return BCrypt.gensalt(this.version.getVersion(), this.strength);
}

使用Spring Security提供的BCrypt工具類生成鹽(salt);然后,根據(jù)鹽和明文密碼生成最終的密文。所謂加鹽,就是在初始化明文數(shù)據(jù)時(shí),由系統(tǒng)自動(dòng)向該明文里添加一些附加數(shù)據(jù),然后散列。引入加鹽機(jī)制的目的是進(jìn)一步提高加密數(shù)據(jù)的安全性,單向散列加密及加鹽思想廣泛應(yīng)用于系統(tǒng)登錄過(guò)程中的密碼生成和校驗(yàn)。

構(gòu)造方法:

public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
	if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
		throw new IllegalArgumentException("Bad strength");
	}
	this.version = version;
	this.strength = (strength == -1) ? 10 : strength;
	this.random = random;
}

從構(gòu)造函數(shù)可知,strength長(zhǎng)度默認(rèn)為10,最小值為BCrypt.MIN_LOG_ROUNDS=4,最大值為BCrypt.MAX_LOG_ROUNDS=31。顯而易見(jiàn),長(zhǎng)度越長(zhǎng),加密算法越復(fù)雜,被惡意破解攻擊的難度越大,但是也會(huì)增加系統(tǒng)負(fù)載,增加加密計(jì)算時(shí)長(zhǎng)和存儲(chǔ)空間。因此需要取得權(quán)衡,默認(rèn)情況下使用Spring Security建議的長(zhǎng)度10即可。

PasswordEncoderFactories

瀏覽一下spring-security-crypto-6.2.3源碼結(jié)構(gòu):

在這里插入圖片描述

不難發(fā)現(xiàn)PasswordEncoderFactories這個(gè)類,采用工廠方法模式,源碼:

public static PasswordEncoder createDelegatingPasswordEncoder() {
	String encodingId = "bcrypt";
	Map<String, PasswordEncoder> encoders = new HashMap();
	encoders.put(encodingId, new BCryptPasswordEncoder());
	encoders.put("ldap", new LdapShaPasswordEncoder());
	encoders.put("MD4", new Md4PasswordEncoder());
	encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
	encoders.put("noop", NoOpPasswordEncoder.getInstance());
	encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
	encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
	encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
	encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
	encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
	encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
	encoders.put("sha256", new StandardPasswordEncoder());
	encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
	encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
	return new DelegatingPasswordEncoder(encodingId, encoders);
}

靜態(tài)方法createDelegatingPasswordEncoder,encoders中存儲(chǔ)每一種密碼加密方案的id和所對(duì)應(yīng)的加密類,如bcrypt對(duì)應(yīng)BcryptPassword。最后,返回代理類DelegatingPasswordEncoder實(shí)例,并且默認(rèn)使用的加密方案是BCryptPasswordEncoder。

DelegatingPasswordEncoder

DelegatingPasswordEncoder,采用代理模式,Spring Security 5.0版本后默認(rèn)的密碼加密方案,主要考慮如下三方面的因素:

  • 兼容性:使用DelegatingPasswordEncoder可以幫助許多使用舊密碼加密方式的系統(tǒng)順利遷移到Spring Security中,它允許在同一個(gè)系統(tǒng)中同時(shí)存在多種不同的密碼加密方案
  • 便捷性:密碼存儲(chǔ)的最佳方案不可能一直不變,使用DelegatingPasswordEncoder作為默認(rèn)的密碼加密方案,當(dāng)需要修改加密方案時(shí),只需要修改很小一部分代碼即可實(shí)現(xiàn)
  • 穩(wěn)定性:作為一個(gè)框架,Spring Security不能經(jīng)常進(jìn)行重大更改,使用Delegating PasswordEncoder可以方便地對(duì)密碼進(jìn)行升級(jí)(自動(dòng)從一個(gè)加密方案升級(jí)到另外一個(gè)加密方案)

屬性如下:

// 默認(rèn)的前綴和后綴,用于包裹將來(lái)生成的加密方案的id
private static final String DEFAULT_ID_PREFIX = "{";
private static final String DEFAULT_ID_SUFFIX = "}";
// 構(gòu)造方法里支持傳入用戶自定義的前綴和后綴
private final String idPrefix;
private final String idSuffix;
// 默認(rèn)的加密方案id
private final String idForEncode;
// 根據(jù)idForEncode從idToPasswordEncoder map中提取出來(lái)的
private final PasswordEncoder passwordEncoderForEncode;
// 保存id和加密方案之間的映射
private final Map<String, PasswordEncoder> idToPasswordEncoder;
// 默認(rèn)的密碼比對(duì)器,當(dāng)根據(jù)密碼加密方案的id無(wú)法找到對(duì)應(yīng)的加密方案時(shí),就會(huì)使用默認(rèn)的密碼比對(duì)器。默認(rèn)類型是UnmappedIdPasswordEncoder
private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();

UnmappedIdPasswordEncoder是一個(gè)內(nèi)部私有類:

private class UnmappedIdPasswordEncoder implements PasswordEncoder {
	@Override
	public String encode(CharSequence rawPassword) {
		// 直接拋出異常
		throw new UnsupportedOperationException("encode is not supported");
	}

	@Override
	public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
		// 并不會(huì)做任何密碼比對(duì)操作,直接拋出異常
		String id = extractId(prefixEncodedPassword);
		throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
	}
}

核心方法encode

public String encode(CharSequence rawPassword) {
	return this.idPrefix + this.idForEncode + this.idSuffix + this.passwordEncoderForEncode.encode(rawPassword);
}

作為一個(gè)代理類,不負(fù)責(zé)具體的加密工作,由加密類來(lái)完成,最后加上類似于{bcrypt}這樣的前綴,不同的前綴表示使用不同的加密算法,即不同的PasswordEncoder實(shí)現(xiàn)類,當(dāng)然也包括自定義的加密類。

核心方法matches

public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
	if (rawPassword == null && prefixEncodedPassword == null) {
		return true;
	}
	String id = extractId(prefixEncodedPassword);
	PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
	if (delegate == null) {
		return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);
	}
	String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
	return delegate.matches(rawPassword, encodedPassword);
}

extractId方法用于從加密字符串中提取出具體的加密方案id,也就是前綴和后綴包裹的字符串,如bcrypt,此方法就不貼出來(lái)了。根據(jù)加密方案id從map集合查找對(duì)應(yīng)的加密算法實(shí)現(xiàn)類,查找失敗則使用默認(rèn)的加密類,即UnmappedIdPasswordEncoder,然后就會(huì)拋出異常。

核心方法upgradeEncoding

public boolean upgradeEncoding(String prefixEncodedPassword) {
	String id = extractId(prefixEncodedPassword);
	if (!this.idForEncode.equalsIgnoreCase(id)) {
		return true;
	} else {
		String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
		return this.idToPasswordEncoder.get(id).upgradeEncoding(encodedPassword);
	}
}

如果當(dāng)前加密字符串所采用的加密方案不是默認(rèn)的BcryptPasswordEncoder ,就會(huì)自動(dòng)進(jìn)行密碼升級(jí),否則就調(diào)用默認(rèn)加密方案的upgradeEncoding方法判斷密碼是否需要升級(jí)。

自定義加密方案

業(yè)務(wù)開(kāi)發(fā)中,如果Spring Security自帶的幾個(gè)加密類都不能滿足需求,或者業(yè)務(wù)場(chǎng)景比較復(fù)雜,需要兼容數(shù)據(jù)庫(kù)歷史未加密字段或加密算法不夠好的字段,則可能需要自定義加密類。

具體來(lái)說(shuō),實(shí)現(xiàn)PasswordEncoder接口類,并重寫3個(gè)方法。比如自定義一個(gè)使用SHA-512加密算法的加密類:

public class Sha512PasswordEncoder implements PasswordEncoder {
	@Override
	public String encode(CharSequence rawPassword) {
		return hashWithSha512(rawPassword.toString());
	}
	
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		String hashedPassword = encode(rawPassword);
		return encodedPassword.equals(hashedPassword);
	}

	@Override
	public boolean upgradeEncoding(String prefixEncodedPassword) {
		// 不需要升級(jí)
		return false;
	}
	
	private String hashWithSha512(String input) {
		StringBuilder result = new StringBuilder();
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-512");
			byte [] digested = md.digest(input.getBytes());
			for (int i = 0; i < digested.length; i++) {
				result.append(Integer.toHexString(0xFF & digested[i]));
			}
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("Bad algorithm");
		}
		return result.toString();
	}
}

最后需要配置一下使用此自定義類,使其生效。

參考

  • 深入淺出Spring Security

到此這篇關(guān)于SpringSecurity中PasswordEncoder的使用的文章就介紹到這了,更多相關(guān)SpringSecurity PasswordEncoder內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評(píng)論