Spring Security作為一個安全框架,其中必然就應(yīng)該帶有安全加密方面的內(nèi)容,今天就帶各位來學(xué)習(xí)Spring Security中的密碼加密機制。
一. 密碼加密簡介
1. 散列加密概述
我們開發(fā)時進行密碼加密,可用的加密手段有很多,比如對稱加密、非對稱加密、信息摘要等。在一般的項目里,常用的就是信息摘要算法,也可以被稱為散列加密函數(shù),或者稱為散列算法、哈希函數(shù)。
這是一種可以從任何數(shù)據(jù)中創(chuàng)建數(shù)字“指紋”的方法,常用的散列函數(shù)有 MD5 消息摘要算法、安全散列算法(Secure Hash Algorithm)等。
2. 散列加密原理
散列函數(shù)通過把消息或數(shù)據(jù)壓縮成摘要信息,使得數(shù)據(jù)量變小,將數(shù)據(jù)的格式固定下來,然后將數(shù)據(jù)打亂混合,再重新創(chuàng)建成一個散列值,從而達到加密的目的。
散列值通常用一個短的隨機字母和數(shù)字組成的字符串來代表,一個好的散列函數(shù)在輸入域中很少出現(xiàn)散列沖突。在散列表和數(shù)據(jù)處理時,如果我們不抑制沖突來區(qū)別數(shù)據(jù),會使得數(shù)據(jù)庫中的記錄很難找到。
但是僅僅使用散列函數(shù)還不夠,如果我們只是單純的使用散列函數(shù)而不做特殊處理,其實是有風(fēng)險的!比如在兩個用戶密碼明文相同時,生成的密文也會相同,這樣就增加了密碼泄漏的風(fēng)險。
所以為了增加密碼的安全性,一般在密碼加密過程中還需要“加鹽”,而所謂的“鹽”可以是一個隨機數(shù),也可以是用戶名。”加鹽“之后,即使密碼的明文相同,用戶生成的密碼密文也不相同,這就可以極大的提高密碼的安全性。
傳統(tǒng)的加鹽方式需要在數(shù)據(jù)庫中利用專門的字段來記錄鹽值,這個字段可以是用戶名字段(因為用戶名唯一),也可以是一個專門記錄鹽值的字段,但這樣的配置比較繁瑣。
3. Spring Security中的密碼處理方案
那么在Spring Security中,對密碼是怎么進行處理的呢?其實Spring Security對密碼的處理方案,有如下3種方式:
對密碼進行明文處理,即不采用任何加密方式;
采用MD5加密方式;
采用哈希算法加密方式。
3. BCryptPasswordEncoder簡介
以上說的是3種密碼處理方案,并不代表只有3種加密算法,這個請大家注意哦!
我們開發(fā)時,用戶表中的密碼通常是使用MD5等不可逆算法加密后存儲,但為了防止彩虹表破解,可以先使用一個特定的字符串(如域名)進行加密,然后再使用一個隨機的salt(鹽值)加密。其中特定的字符串是程序代碼中固定的,salt是每個密碼單獨隨機的,我們一般會給用戶表加一個字段單獨存儲,但這樣比較麻煩。
而BCrypt算法卻可以隨機生成salt并混入最終加密后的密碼,驗證時也無需單獨提供之前的salt,從而無需單獨處理salt。不同于 Shiro 中需要自己處理密碼加鹽,在 Spring Security 中,BCrypt Password Encoder 本身就自帶了鹽,所以處理起來非常方便。
另外BCryptPasswordEncoder使用BCrypt強哈希函數(shù),我們在使用時可以選擇提供strength和SecureRandom參數(shù)。strength值(取值在4~31之間,默認為10)越大,則密鑰的迭代次數(shù)就越多,密鑰迭代次數(shù)為2^strength。
二. 利用BCryptPasswordEncoder進行加密
了解了這些基本的理論知識之后,壹哥 就帶各位進行代碼實現(xiàn)啦。
我們繼續(xù)在之前的案例基礎(chǔ)之上進行本案例的代碼實現(xiàn),所以項目創(chuàng)建過程略過,請參考之前的章節(jié)內(nèi)容。
1. 編寫register接口
為了方便測試,我們首先在UserController中編寫一個register接口,用于注冊一個新用戶,在添加用戶時對密碼進行加密。
2. 配置密碼加密算法
接下來我們在Security Config配置類中,配置到底該采用哪種密碼加密算法。我們在Spring Boot環(huán)境中是非常容易實現(xiàn)加密算法配置的,只需要創(chuàng)建一個Password Encoder對象即可。
注意:這里我們可以有多種創(chuàng)建PasswordEncoder對象的寫法!并且別忘了把“/user/register”注冊接口直接放行,注冊接口不應(yīng)該攔截。
BCryptPasswordEncoder加解密原理
我前面說過,BCrypt Password Encoder加密時,每次都會隨機生成一個鹽值混入到密碼中,以此保證即使密碼明文一樣,最終得到的密文也不一樣。但是這時候問題就來了,這個鹽值是BCryptPasswordEncoder自動生成的,我們程序員也不知道,那到時候怎么進行密碼的比對呢?因為比對密碼時,肯定也需要把明文添加鹽值后再加密才能比對??!別急,往下看!
BCrypt Password Encoder調(diào)用 encode(..) 方法對密碼明文加密時,每次都會隨機的生成一個鹽值,把這個鹽值和明文再一起混淆最終得到密碼的密文,所以這個最終的密文分為兩部分:鹽值和最終加密的結(jié)果。
BCryptPasswordEncoder調(diào)用matches(..)方法對比的時候,會利用自己特定的方法,先從密文里面拿出鹽值,然后利用該鹽值對密碼的明文進行加密得到一個新的密文,最后利用這個新生成的密文和之前的密文進行對比,這樣就能知道傳遞過來的密碼是否和存儲的密碼是否一樣了。
三. 利用其他Encoder進行加密實現(xiàn)
1. MessageDigestPasswordEncoder的用法
除了可以使用上面提到的默認的BCrypt Password Encoder加密方案之外,我們還可以使用Message Digest Password Encoder方案,該方案內(nèi)部是采用"MD5"、"SHA-1"、"SHA-256"等信息摘要算法實現(xiàn)的加密,所以我們需要在構(gòu)造的時候傳入MD5等算法名稱字符串。這個配置在SecurityConfig類中實現(xiàn)即可!
利用Message Digest Password Encoder 的encode(..) 加密方法,每次都會隨機生成鹽值,所以對相同的明文進行多次加密,每次得到的結(jié)果是不一樣的。
Message Digest Password Encoder這個加密的最終結(jié)果也是分為兩部分:鹽值 + MD5 (password+鹽值)。那么當(dāng)我們調(diào)用 matches(..) 方法對比密碼的時候,也是先從密文中得到鹽值,然后利用該鹽值再加密明文,最后利用這個新生成的密文和之前的密文進行對比。
2. DelegatingPasswordEncoder的用法
我們還有另一種加密實現(xiàn)寫法,就是利用Delegating Password Encoder來進行實現(xiàn)。
Delegating Password Encoder是Spring Security 推出的一套兼容方案,該方案會根據(jù)加密類型的id字符串(idFor Encode),去自身緩存的所有加密方式中(idTo Password Encoder)取出對應(yīng)的加密方案對象,然后對明文進行加密和密文的對比。
(責(zé)任編輯:代碼如詩) |