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