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

SpringSecurity中PasswordEncoder的使用

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

概述

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

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

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

反查表、彩虹表

上文提到一些已經(jīng)不推薦使用、逐漸被淘汰的算法,如MD5、SHA-1。因為不管是MD5還是SHA-1算法,對于給定的某個字符串(密碼),經(jīng)過哈希函數(shù)計算之后得到的結(jié)果都是固定的。比如admin經(jīng)過MD5計算(有16位和32位之分,這里用的是16位)結(jié)果始終是7a57a5a743894a0eroot經(jīng)過SHA-1計算后結(jié)果始終是dc76e9f0c0006e8f919e0c515c66dbba3982f785。

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

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

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

Spring Security

實例

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

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 {
	// 用來對明文密碼進(jìn)行加密
	String encode(CharSequence rawPassword);
	// 用來進(jìn)行密碼比對
	boolean matches(CharSequence rawPassword, String encodedPassword);
	// 用來判斷當(dāng)前密碼是否需要升級,默認(rèn)返回false表示不需要升級
	default boolean upgradeEncoding(String encodedPassword) {
		return false;
	}
}

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

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

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

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

  • NoOpPasswordEncoder:密碼明文存儲,不可用于生產(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ù)時,由系統(tǒng)自動向該明文里添加一些附加數(shù)據(jù),然后散列。引入加鹽機(jī)制的目的是進(jìn)一步提高加密數(shù)據(jù)的安全性,單向散列加密及加鹽思想廣泛應(yīng)用于系統(tǒng)登錄過程中的密碼生成和校驗。

構(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長度默認(rèn)為10,最小值為BCrypt.MIN_LOG_ROUNDS=4,最大值為BCrypt.MAX_LOG_ROUNDS=31。顯而易見,長度越長,加密算法越復(fù)雜,被惡意破解攻擊的難度越大,但是也會增加系統(tǒng)負(fù)載,增加加密計算時長和存儲空間。因此需要取得權(quán)衡,默認(rèn)情況下使用Spring Security建議的長度10即可。

PasswordEncoderFactories

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

在這里插入圖片描述

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

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中存儲每一種密碼加密方案的id和所對應(yīng)的加密類,如bcrypt對應(yīng)BcryptPassword。最后,返回代理類DelegatingPasswordEncoder實例,并且默認(rèn)使用的加密方案是BCryptPasswordEncoder。

DelegatingPasswordEncoder

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

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

屬性如下:

// 默認(rèn)的前綴和后綴,用于包裹將來生成的加密方案的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中提取出來的
private final PasswordEncoder passwordEncoderForEncode;
// 保存id和加密方案之間的映射
private final Map<String, PasswordEncoder> idToPasswordEncoder;
// 默認(rèn)的密碼比對器,當(dāng)根據(jù)密碼加密方案的id無法找到對應(yīng)的加密方案時,就會使用默認(rèn)的密碼比對器。默認(rèn)類型是UnmappedIdPasswordEncoder
private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();

UnmappedIdPasswordEncoder是一個內(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) {
		// 并不會做任何密碼比對操作,直接拋出異常
		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);
}

作為一個代理類,不負(fù)責(zé)具體的加密工作,由加密類來完成,最后加上類似于{bcrypt}這樣的前綴,不同的前綴表示使用不同的加密算法,即不同的PasswordEncoder實現(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,此方法就不貼出來了。根據(jù)加密方案id從map集合查找對應(yīng)的加密算法實現(xiàn)類,查找失敗則使用默認(rèn)的加密類,即UnmappedIdPasswordEncoder,然后就會拋出異常。

核心方法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 ,就會自動進(jìn)行密碼升級,否則就調(diào)用默認(rèn)加密方案的upgradeEncoding方法判斷密碼是否需要升級。

自定義加密方案

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

具體來說,實現(xiàn)PasswordEncoder接口類,并重寫3個方法。比如自定義一個使用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) {
		// 不需要升級
		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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評論