Spring Security(3)----用戶密碼加密實現


前言

Spring Security系列二 用戶登錄認證數據庫實現中,我們已經把對用戶的認證改成了數據庫實現,功能上雖然完成了,但是用戶的密碼卻都是以明文保存的,這在實際項目中安全系數上會有所欠缺。在本章中我們將實現如何對用戶的密碼進行加密。

Spring Security中的密碼加密

Spring Security中,對密碼的加密都是由PasswordEncoder來完成的。

那什么時候會調用這個PasswordEncoder呢?這就要回到前面實現數據庫登錄認證時的DaoAuthenticationProvider了。在DaoAuthenticationProvider中,除了UserDetailsService之外還有其它的幾個屬性,其中一個就是PasswordEncoderUserDetailsService前面我們已經實現了,現在要實現PasswordEncoder,密碼加密功能主要就是靠它來完成。

Spring Security中的PasswordEncoder

其實在Spring Security中,已經對PasswordEncoder有了很多實現,包括md5加密、SHA-256加密等等,一般情況下我們只要直接拿來用就可以了。

查看類DaoAuthenticationProvidersetPasswordEncoder方法:

public void setPasswordEncoder(Object passwordEncoder) { ... }

會發現參數類型居然是Object類型,這是因為在內置的PasswordEncoder中,又分了兩條路線,應該是隨着版本的更新優化而衍生的,但為了兼容老版本所以兩個都保留了下來,這里就都分別介紹一下。

老的PasswordEncoder

具體是指接口:org.springframework.security.authentication.encoding.PasswordEncoder

之所以說它老是因為在該接口上已經標了@Deprecated注解不推薦使用了,但相應的實現類卻沒有標注,所以目前使用上依然是相當廣泛的,很多人可能並不知道已經@Deprecated了。

它的類圖結構如下:

PasswordEncoder類結構圖

可以看到有很多常用的PasswordEncoder已經有實現了,這里拿最常用的Md5PasswordEncoder來做示例。

想要使用密碼加密就必須指定使用哪個PasswordEncoder,但是在AuthenticationManagerBuilder中並沒有可以快速指定PasswordEncoder的地方,所以這里必須自己聲明AuthenticationProvider,然后設置UserDetailsServicePasswordEncoder,具體代碼如下:

@Bean public UserDetailsService userDetailsService() { return new CustomUserDetailsService(); } @Bean public PasswordEncoder passwordEncoder(){ return new Md5PasswordEncoder(); } @Bean public AuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService()); authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.userDetailsService(userDetailsService()); auth.authenticationProvider(authenticationProvider()); }

需要注意在configure中設置了AuthenticationProvider就要把原先的auth.userDetailsService(userDetailsService())去掉,不然就會有兩個userDetailsService的調用和認證,結果必然是一次正確一次不正確,返回你預期之外的結果。

新的PasswordEncoder

具體是指接口:org.springframework.security.crypto.password.PasswordEncoder,這是spring當前推薦使用的接口。

它的類圖如下:

新的PasswordEncoder類圖

實現類只有三個,簡單明了,但加密安全性卻提高了。

NoOpPasswordEncoder不多說了,啥也不做按原文本處理,相當於不加密。

StandardPasswordEncoder 1024次迭代的SHA-256散列哈希加密實現,並使用一個隨機8字節的salt。

BCryptPasswordEncoder 使用BCrypt的強散列哈希加密實現,並可以由客戶端指定加密的強度strength,強度越高安全性自然就越高,默認為10.

Spring的注釋中,明確寫明了如果是開發一個新的項目,BCryptPasswordEncoder是較好的選擇。

 * If you are developing a new system, * {@link org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder} is a better * choice both in terms of security and interoperability with other languages.

代碼示例:

@Bean public org.springframework.security.crypto.password.PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService()); authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.userDetailsService(userDetailsService()); auth.authenticationProvider(authenticationProvider()); }

給密碼加點鹽 salt

不得不說salt這名字取的很貼切。

在很多時候我們可能需要給密碼加點指定的前綴或后綴,以防止像123456這類簡單的密碼被反向破解,這時候就會用到SaltSource了。

其實SaltSource隨着PasswordEncoder的更換目前已是不推薦使用了,但是有必要了解一下它,以及它背后的目的是什么,以實現更好的密碼安全性。

SaltSource類圖如下:

SaltSource類圖

SaltSource的目的就是混淆一下密碼然后再進行加密,防止加密后的字符串被反向破解。像ReflectionSaltSource可以指定對象的某個屬性值添加到密碼中以增加安全性。

這里為簡單起見,我們自己實現一個SaltSource,在密碼中加固定的字母abc

/** * Created by liyd on 16/11/26. */ public class CustomSaltSource implements SaltSource { @Override public Object getSalt(UserDetails userDetails) { return "abc"; } }

然后指定使用CustomSaltSource

@Bean public SaltSource saltSource() { return new CustomSaltSource(); } @Bean public AuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService()); authenticationProvider.setPasswordEncoder(passwordEncoder()); authenticationProvider.setSaltSource(saltSource()); return authenticationProvider; }

啟動項目,打上斷點調試,發現在我們的密碼123456后面加上了abc

處理后密碼

自然加密后的字符串也相應變了,加強了密碼的安全性。

需要注意SaltSource只針對老的PasswordEncoder而言,新的PasswordEncoder已經不需要使用SaltSource來加強密碼的安全性了,因為它的強度可以由用戶指定,強度不同加密后的字符串自然也不同,安全性已經足夠了,就算你想加也會拋出下面的異常:

java.lang.IllegalArgumentException: Salt value must be null when used with crypto module PasswordEncoder

密碼的保存

以上說了密碼的加密校驗,有個前提當然是你在保存數據的時候密碼加密方式得和這個保持一致,這個也不用自己實現,既然已經有了直接把PasswordEncoder拿來用就行:

@Bean public AuthenticationProvider authenticationProvider() { //這里只做如何使用passwordEncoder與校驗保持一致示例 密碼輸出 String password = passwordEncoder().encode("123456"); System.out.println(password); ... }

附件列表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM