国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Spring Security保護(hù)用戶密碼常用方法詳解

瀏覽:31日期:2023-08-15 15:10:50

1. 前言

本節(jié)將對(duì) Spring Security 中的密碼編碼進(jìn)行一些探討。

2. 不推薦使用md5

首先md5 不是加密算法,是哈希摘要。以前通常使用其作為密碼哈希來(lái)保護(hù)密碼。由于彩虹表的出現(xiàn),md5 和sha1之類的摘要算法都已經(jīng)不安全了。如果有不相信的同學(xué) 可以到一些解密網(wǎng)站 如 cmd5 網(wǎng)站嘗試解密 你會(huì)發(fā)現(xiàn) md5 和 sha1 是真的非常容易被破解。

3. Spring Security中的密碼算法

ObjectProvider<PasswordEncoder>參數(shù)。這里的PasswordEncoder`就是我們對(duì)密碼進(jìn)行編碼的工具接口。該接口只有兩個(gè)功能:一個(gè)是匹配驗(yàn)證。另一個(gè)是密碼編碼。

Spring Security保護(hù)用戶密碼常用方法詳解

上圖就是Spring Security 提供的org.springframework.security.crypto.password.PasswordEncoder一些實(shí)現(xiàn),有的已經(jīng)過(guò)時(shí)。其中我們注意到一個(gè)叫委托密碼編碼器的實(shí)現(xiàn) 。

3.1 委托密碼編碼器 DelegatingPasswordEncoder

什么是委托(Delegate)?就是甲方交給乙方的活。乙方呢手里又很多的渠道,但是乙方光想賺差價(jià)又不想干活。所以乙方根據(jù)一些規(guī)則又把活委托給了別人,讓別人來(lái)干。這里的乙方就是DelegatingPasswordEncoder 。該類維護(hù)了以下清單:

final String idForEncode 通過(guò)id來(lái)匹配編碼器,該id不能是{} 包括的。DelegatingPasswordEncoder 初始化傳入,用來(lái)提供默認(rèn)的密碼編碼器。 final PasswordEncoder passwordEncoderForEncode 通過(guò)上面idForEncode所匹配到的PasswordEncoder 用來(lái)對(duì)密碼進(jìn)行編碼。 final Map&lt;String, PasswordEncoder&gt; idToPasswordEncoder 用來(lái)維護(hù)多個(gè)idForEncode與具體PasswordEncoder的映射關(guān)系。DelegatingPasswordEncoder 初始化時(shí)裝載進(jìn)去,會(huì)在初始化時(shí)進(jìn)行一些規(guī)則校驗(yàn)。 PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder() 默認(rèn)的密碼匹配器,上面的Map中都不存在就用它來(lái)執(zhí)行matches方法進(jìn)行匹配驗(yàn)證。這是一個(gè)內(nèi)部類實(shí)現(xiàn)。

DelegatingPasswordEncoder 編碼方法:

@Override public String encode(CharSequence rawPassword) { return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword); }

從上面源碼可以看出來(lái)通過(guò)DelegatingPasswordEncoder 編碼后的密碼是遵循一定的規(guī)則的,遵循{idForEncode}encodePassword 。也就是前綴{} 包含了編碼的方式再拼接上該方式編碼后的密碼串。

DelegatingPasswordEncoder 密碼匹配方法:

@Override 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); }

密碼匹配通過(guò)傳入原始密碼和遵循{idForEncode}encodePassword規(guī)則的密碼編碼串。通過(guò)獲取編碼方式id (idForEncode) 來(lái)從 DelegatingPasswordEncoder中的映射集合idToPasswordEncoder中獲取具體的PasswordEncoder進(jìn)行匹配校驗(yàn)。找不到就使用UnmappedIdPasswordEncoder 。

這就是 DelegatingPasswordEncoder 的工作流程。那么DelegatingPasswordEncoder 在哪里實(shí)例化呢?

3.2 密碼器靜態(tài)工廠PasswordEncoderFactories

從名字上就看得出來(lái)這是個(gè)工廠啊,專門(mén)制造 PasswordEncoder 。而且還是個(gè)靜態(tài)工廠只提供了初始化DelegatingPasswordEncoder的方法:

@SuppressWarnings('deprecation') public static PasswordEncoder createDelegatingPasswordEncoder() { String encodingId = 'bcrypt'; Map<String, PasswordEncoder> encoders = new HashMap<>(); encoders.put(encodingId, new BCryptPasswordEncoder()); encoders.put('ldap', new org.springframework.security.crypto.password.LdapShaPasswordEncoder()); encoders.put('MD4', new org.springframework.security.crypto.password.Md4PasswordEncoder()); encoders.put('MD5', new org.springframework.security.crypto.password.MessageDigestPasswordEncoder('MD5')); encoders.put('noop', org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()); encoders.put('pbkdf2', new Pbkdf2PasswordEncoder()); encoders.put('scrypt', new SCryptPasswordEncoder()); encoders.put('SHA-1', new org.springframework.security.crypto.password.MessageDigestPasswordEncoder('SHA-1')); encoders.put('SHA-256', new org.springframework.security.crypto.password.MessageDigestPasswordEncoder('SHA-256')); encoders.put('sha256', new org.springframework.security.crypto.password.StandardPasswordEncoder()); return new DelegatingPasswordEncoder(encodingId, encoders); }

從上面可以非常具體地看出來(lái)DelegatingPasswordEncoder提供的密碼編碼方式。默認(rèn)采用了bcrypt 進(jìn)行編碼。我們可終于明白了為什么上一文中我們使用 {noop12345} 能和我們前臺(tái)輸入的12345匹配上。這么搞有什么好處呢?這可以實(shí)現(xiàn)一個(gè)場(chǎng)景,如果有一天我們對(duì)密碼編碼規(guī)則進(jìn)行替換或者輪轉(zhuǎn)。現(xiàn)有的用戶不會(huì)受到影響。 那么Spring Security 是如何配置密碼編碼器PasswordEncoder 呢?

4. Spring Security 加載 PasswordEncoder 的規(guī)則

我們?cè)赟pring Security配置適配器WebSecurityConfigurerAdapter(該類我以后的文章會(huì)仔細(xì)分析 可通過(guò)https://felord.cn 來(lái)及時(shí)獲取相關(guān)信息)找到了引用PasswordEncoderFactories的地方,一個(gè)內(nèi)部 PasswordEncoder實(shí)現(xiàn) LazyPasswordEncoder。從源碼上看該類是懶加載的只有用到了才去實(shí)例化。在該類的內(nèi)部方法中發(fā)現(xiàn)了 PasswordEncoder 的規(guī)則。

// 獲取最終干活的PasswordEncoder private PasswordEncoder getPasswordEncoder() { if (this.passwordEncoder != null) {return this.passwordEncoder; } PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class); if (passwordEncoder == null) {passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); } this.passwordEncoder = passwordEncoder; return passwordEncoder; } // 從Spring IoC容器中獲取Bean 有可能獲取不到 private <T> T getBeanOrNull(Class<T> type) { try {return this.applicationContext.getBean(type); } catch(NoSuchBeanDefinitionException notFound) {return null; } }

上面的兩個(gè)方法總結(jié):如果能從從Spring IoC容器中獲取PasswordEncoder的Bean就用該Bean作為編碼器,沒(méi)有就使用DelegatingPasswordEncoder 。默認(rèn)是 bcrypt 方式。文中多次提到該算法。而且還是Spring Security默認(rèn)的。那么它到底是什么呢?

5. bcrypt 編碼算法

這里簡(jiǎn)單提一下bcrypt, bcrypt使用的是布魯斯·施內(nèi)爾在1993年發(fā)布的 Blowfish 加密算法。bcrypt 算法將salt隨機(jī)并混入最終加密后的密碼,驗(yàn)證時(shí)也無(wú)需單獨(dú)提供之前的salt,從而無(wú)需單獨(dú)處理salt問(wèn)題。加密后的格式一般為:

$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa其中:$是分割符,無(wú)意義;2a是bcrypt加密版本號(hào);10是cost的值;而后的前22位是salt值;再然后的字符串就是密碼的密文了。

5.1 bcrypt 特點(diǎn)

bcrypt有個(gè)特點(diǎn)就是非常慢。這大大提高了使用彩虹表進(jìn)行破解的難度。也就是說(shuō)該類型的密碼暗文擁有讓破解者無(wú)法忍受的時(shí)間成本。同時(shí)對(duì)于開(kāi)發(fā)者來(lái)說(shuō)也需要注意該時(shí)長(zhǎng)是否能超出系統(tǒng)忍受范圍內(nèi)。通常是MD5的數(shù)千倍。同樣的密碼每次使用bcrypt編碼,密碼暗文都是不一樣的。 也就是說(shuō)你有兩個(gè)網(wǎng)站如果都使用了bcrypt 它們的暗文是不一樣的,這不會(huì)因?yàn)橐粋€(gè)網(wǎng)站泄露密碼暗文而使另一個(gè)網(wǎng)站也泄露密碼暗文。所以從bcrypt的特點(diǎn)上來(lái)看,其安全強(qiáng)度還是非常有保證的。

6. 總結(jié)

今天我們對(duì)Spring Security中的密碼編碼進(jìn)行分析。發(fā)現(xiàn)了默認(rèn)情況下使用bcrypt進(jìn)行編碼。而密碼驗(yàn)證匹配則通過(guò)密碼暗文前綴中的加密方式id控制。你也可以向Spring IoC容器注入一個(gè)PasswordEncoder類型的Bean 來(lái)達(dá)到自定義的目的。我們還對(duì)bcrypt算法進(jìn)行一些簡(jiǎn)單了解,對(duì)其特點(diǎn)進(jìn)行了總結(jié)。后面我們會(huì)Spring Security進(jìn)行進(jìn)一步學(xué)習(xí)。關(guān)于上一篇文章的demo我也已經(jīng)替換成了數(shù)據(jù)庫(kù)管理用戶。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 措勤县| 汾西县| 伊金霍洛旗| 桂林市| 博白县| 禄丰县| 武鸣县| 郧西县| 房山区| 巴彦淖尔市| 双桥区| 正安县| 湄潭县| 辉南县| 孝感市| 永胜县| 霍城县| 太原市| 唐海县| 惠安县| 大理市| 四平市| 平南县| 株洲市| 丁青县| 张家口市| 蛟河市| 霍城县| 黎城县| 阳曲县| 唐河县| 孟津县| 佛山市| 婺源县| 凯里市| 舞钢市| 昌吉市| 东城区| 尼玛县| 德格县| 郑州市|