Spring Security-从入门到精通-PasswordEncoder详解
PasswordEncoder接口
- PasswordEncoder是一个密码解析器
Spring Security封装了如bcrypt, PBKDF2, scrypt, Argon2等主流适应性单向加密方法( adaptive one-way functions),用以进行密码存储和校验。单向校验安全性高,但开销很大,单次密码校验耗时可能高达1秒,故针对高并发性能要求较强的大型信息系统,Spring Security更推荐选择如:session, OAuth,Token等开销很小的短期加密策略(short term credential)实现系统信息安全。
package org.springframework.security.crypto.password; public interface PasswordEncoder { String encode(CharSequence var1); boolean matches(CharSequence var1, String var2); default boolean upgradeEncoding(String encodedPassword) { return false; } }
BCryptPasswordEncoder实现
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.security.crypto.bcrypt; import java.security.SecureRandom; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.crypto.password.PasswordEncoder; public class BCryptPasswordEncoder implements PasswordEncoder { private Pattern BCRYPT_PATTERN; private final Log logger; private final int strength; private final BCryptPasswordEncoder.BCryptVersion version; private final SecureRandom random; public BCryptPasswordEncoder() { this(-1); } public BCryptPasswordEncoder(int strength) { this(strength, (SecureRandom)null); } public BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion version) { this(version, (SecureRandom)null); } public BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion version, SecureRandom random) { this(version, -1, random); } public BCryptPasswordEncoder(int strength, SecureRandom random) { this(BCryptPasswordEncoder.BCryptVersion.$2A, strength, random); } public BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion version, int strength) { this(version, strength, (SecureRandom)null); } public BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion version, int strength, SecureRandom random) { this.BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}"); this.logger = LogFactory.getLog(this.getClass()); if (strength == -1 || strength >= 4 && strength <= 31) { this.version = version; this.strength = strength == -1 ? 10 : strength; this.random = random; } else { throw new IllegalArgumentException("Bad strength"); } } public String encode(CharSequence rawPassword) { String salt; if (this.random != null) { salt = BCrypt.gensalt(this.version.getVersion(), this.strength, this.random); } else { salt = BCrypt.gensalt(this.version.getVersion(), this.strength); } 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; } } public boolean upgradeEncoding(String encodedPassword) { if (encodedPassword != null && encodedPassword.length() != 0) { Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword); if (!matcher.matches()) { throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword); } else { int strength = Integer.parseInt(matcher.group(2)); return strength < this.strength; } } else { this.logger.warn("Empty encoded password"); return false; } } public static enum BCryptVersion { $2A("$2a"), $2Y("$2y"), $2B("$2b"); private final String version; private BCryptVersion(String version) { this.version = version; } public String getVersion() { return this.version; } } }
测试类
package com.mangoubiubiu.security; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @SpringBootTest class SecurityDemo1ApplicationTests { @Test void contextLoads() { PasswordEncoder pw =new BCryptPasswordEncoder(); String encode=pw.encode("123456"); System.out.println(encode); boolean matches = pw.matches("123456", encode); System.out.println(matches); } }