淺析如何使用SpringSecurity實現密碼加密


  Spring Security提供了多種密碼加密方案,官方推薦使用BCryptPasswordEncoder,BCryptPasswordEncoder使用BCrypt強哈希函數,開發者在使用時可以選擇提供strength和SecureRandom實例。strength越大,密鑰的迭代次數越多,密鑰迭代次數為2^strength。strength取值在4~31之間,默認為10。

  在Spring boot中配置密碼加密非常容易,只需要修改上文配置的PasswordEnoder這個Bean的實現即可:

@Bean PasswordEncoder passwordEncoder(){ //參數10就是strength,即密鑰的迭代次數(默認為10)
    return new BCryptPasswordEncoder(10); }

  一般情況下,用戶信息是存儲在數據庫中的,因此需要在用戶注冊時對密碼進行加密處理,代碼如下:

@Service public class RegService { public int reg(String username, String password){ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10); String encodePassword = encoder.encode(password); return saveToDb(username, password); } }

  用戶將密碼從前端傳來之后,通過調用BCryptPasswordEncoder實例中的encode方法對密碼進行加密處理,加密完成后將密文存入數據庫。

一、Spring Security實現密碼加密方法

  首先,Spring Security提供了強大的加密工具PasswordEncoder,PasswordEncoder接口的代碼如下:

public interface PasswordEncoder { String encode(CharSequence var1); boolean matches(CharSequence var1, String var2); default boolean upgradeEncoding(String encodedPassword) { return false; } }

  該接口下定義了三個方法,encode是對加密數據的加密方法,而Boolean類型的match方法是用來驗證密碼和加密后密碼是否一致的,如果一致則返回true。和authentication.encoding包中的PasswordEncoder接口相比,詳細的可以到authentication.encoding包下查找相應的代碼,這里就不列舉了。

二、BCryptPasswordEncoder類

  Spring Security提供了BCryptPasswordEncoder類,該類實現了Spring的PasswordEncoder接口,使用BCrypt強哈希方法來對密碼進行加密,通過BCrypt強哈希方法每一次加密的結果都不一樣(補充說明:即使不同的用戶注冊時輸入相同的密碼,存入數據庫的密文密碼也會不同。),我們可以看看BCryptPasswordEncoder的源碼:

public class BCryptPasswordEncoder implements PasswordEncoder { private Pattern BCRYPT_PATTERN; private final Log logger; private final int strength; private final SecureRandom random; public BCryptPasswordEncoder() { this(-1); } public BCryptPasswordEncoder(int strength) { this(strength, (SecureRandom)null); } public BCryptPasswordEncoder(int strength, SecureRandom random) { this.BCRYPT_PATTERN = Pattern.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}"); this.logger = LogFactory.getLog(this.getClass()); if (strength == -1 || strength >= 4 && strength <= 31) { this.strength = strength; this.random = random; } else { throw new IllegalArgumentException("Bad strength"); } } public String encode(CharSequence rawPassword) { String salt; if (this.strength > 0) { if (this.random != null) { salt = BCrypt.gensalt(this.strength, this.random); } else { salt = BCrypt.gensalt(this.strength); } } else { salt = BCrypt.gensalt(); } return BCrypt.hashpw(rawPassword.toString(), salt); } public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword != null && encodedPassword.length() != 0) { if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) { this.logger.warn("Encoded password does not look like BCrypt"); return false; } else { return BCrypt.checkpw(rawPassword.toString(), encodedPassword); } } else { this.logger.warn("Empty encoded password"); return false; } } }

  BCryptPasswordEncoder類實現了PasswordEncoder 接口下方法,實現了方法encode,方法encode采用強哈希的BCrypt方式進行加密。

三、應用

  我們在編寫用戶實體類的時候,可能會對密碼password進行加密,這時候我們就可以編寫一個加密的方法,例如:

public void setEncodePassword(String password){ //PasswordEncoder是一個接口,而BCryptPasswordEncoder實現了這個接口,里面有一個encode方法是對面進行加密
    PasswordEncoder encoder = new BCryptPasswordEncoder(); String encodePWD =encoder.encode(password); //加密完之后賦值給encodePWD
    this.password = encodePWD; }
...... import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @Service @AllArgsConstructor @Slf4j public class UserService { private UserDao userDao; public Boolean login(User user) { User checkedUser = userDao.checkPhoneRegister(user.getPhoneNum()); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); if(checkedUser == null) { String encode = encoder.encode(user.getPassword()); // 加密密碼
 user.setPassword(encode); userDao.insert(user); return true; } // 判斷密碼是否正確
        Boolean correct = encoder.matches(user.getPassword(), checkedUser.getPassword()); return correct; } }

  用起來就很方便哦。

四、在 WebSecurityConfig 校驗登錄

  在 WebSecurityConfig 增加如下代碼:

  @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth //配置 UserDetailsService 實現類,實現自定義登錄校驗
 .userDetailsService(dbUserDetailService) //配置密碼加密規則
 .passwordEncoder(passwordEncoder()); } /** * 密碼加密,必須為 @Bean ,否則報錯 * 作用:實例化密碼加密規則,該規則首先會校驗數據庫中存儲的密碼是否符合其規則(經過 BCryptPasswordEncoder 加密的密碼 * 的字符串符合一定的規則): * 1.若不符合,直接報錯; * 2.若符合,則會將前端傳遞的密碼經過 BCryptPasswordEncoder 加密,再和數據庫中的密碼進行比對,一樣則通過 * 所以,這里要求,我們存入進數據庫的密碼不能是明文,而必須是經過 BCryptPasswordEncoder 加密后,才能存入數據庫 */ @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }

1、BCryptPasswordEncoder相關知識:

  用戶表的密碼通常使用MD5等不可逆算法加密后存儲,為防止彩虹表破解更會先使用一個特定的字符串(如域名)加密,然后再使用一個隨機的salt(鹽值)加密。

  特定字符串是程序代碼中固定的,salt是每個密碼單獨隨機,一般給用戶表加一個字段單獨存儲,比較麻煩。

  BCrypt算法將salt隨機並混入最終加密后的密碼,驗證時也無需單獨提供之前的salt,從而無需單獨處理salt問題。

2、BCryptPasswordEncoder 是在哪里使用的?

(1)登錄時用到了 DaoAuthenticationProvider

  它有一個方法 additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication),此方法用來校驗從數據庫取得的用戶信息和用戶輸入的信息是否匹配。

(2)在注冊時,需要對用戶密碼加密

  應用 BCryptPasswordEncoder 之后,明文密碼是無法被識別的,就會校驗失敗,只有存入密文密碼才能被正常識別。所以,應該在注冊時對用戶密碼進行加密。

private String encryptPassword(String password) { // BCryptPasswordEncoder 加密
    return new BCryptPasswordEncoder().encode(password); }

 


免責聲明!

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



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