【Spring Security】 PasswordEncoder
【Spring Security】 PasswordEncoder
Metadata
title: 【Spring Security】 PasswordEncoder
date: 2023-02-02 13:52
tags:
- 行动阶段/完成
- 主题场景/组件
- 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
- 细化主题/Module/SpringSecurity
categories:
- SpringSecurity
keywords:
- SpringSecurity
description: 【Spring Security】 PasswordEncoder
【Spring Security】 PasswordEncoder
在之前使用用户名密码进行认证的时候,我们注入了一个注入密码解析器到 [IOC] 中,DaoAuthenticationProvider 就可以获取到这个密码解析器,并使用它进行输入密码和用户名密码校验,那么它具体是怎么加载和执行的呢? 接下里深入了解下
/**
* 注入密码解析器到IOC中
*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
源码分析
PasswordEncoder
PasswordEncoder 是一个接口,他有众多实现类,这些实现类可以理解为 Security 默认支持的密码解析器。
PasswordEncoder 定义了三个方法,用于加密和密码验证。
public interface PasswordEncoder {
/**
* 编码原始密码
*/
String encode(CharSequence rawPassword);
/**
* 验证从存储中获取的编码密码是否与提交的原始密码匹配。密码也经过编码。如果密码匹配,则返回 true,
* 如果不匹配,则返回 false。存储的密码本身应该永远不会被解码
*/
boolean matches(CharSequence rawPassword, String encodedPassword);
/**
* 升级编码
*/
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
BCryptPasswordEncoder
BCryptPasswordEncoder 是使用 BCrypt 强散列函数的 PasswordEncoder 的实现类。
Bcrypt 加密算法,会加入随机盐,相同密码加密后生成的字符串都不一样,比较安全。
$2a$10$iyfe/cmw9PT7gVoZnLqrKOhUI.pntwiWBKmFU8TR.EM6.vS3/y5MG
BCryptPasswordEncoder 源码:
public class BCryptPasswordEncoder implements PasswordEncoder {
private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
private final Log logger = LogFactory.getLog(getClass());
private final int strength;
private final BCryptVersion version;
private final SecureRandom random;
public BCryptPasswordEncoder() {
this(-1);
}
/**
* @param strength 默认值为10,可选值为4-31;
*/
public BCryptPasswordEncoder(int strength) {
this(strength, null);
}
/**
* @param version 版本 默认值为$2a,可选值为$2a, $2b, $2y;
*/
public BCryptPasswordEncoder(BCryptVersion version) {
this(version, null);
}
/**
* @param random 生成加密基本的随机数,SecureRandom的实例,默认值为空;
*/
public BCryptPasswordEncoder(BCryptVersion version, SecureRandom random) {
this(version, -1, random);
}
public BCryptPasswordEncoder(int strength, SecureRandom random) {
this(BCryptVersion.$2A, strength, random);
}
public BCryptPasswordEncoder(BCryptVersion version, int strength) {
this(version, strength, null);
}
/**
*
*/
public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
throw new IllegalArgumentException("Bad strength");
}
this.version = version;
this.strength = (strength == -1) ? 10 : strength;
this.random = random;
}
// 加密
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
String salt = getSalt();
return BCrypt.hashpw(rawPassword.toString(), salt);
}
// 获取随机盐
private String getSalt() {
if (this.random != null) {
return BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
}
return BCrypt.gensalt(this.version.getVersion(), this.strength);
}
// 校验密码
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
if (encodedPassword == null || encodedPassword.length() == 0) {
this.logger.warn("Empty encoded password");
return false;
}
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
//
@Override
public boolean upgradeEncoding(String encodedPassword) {
if (encodedPassword == null || encodedPassword.length() == 0) {
this.logger.warn("Empty encoded password");
return false;
}
Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);
if (!matcher.matches()) {
throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword);
}
int strength = Integer.parseInt(matcher.group(2));
return strength < this.strength;
}
/**
* Stores the default bcrypt version for use in configuration.
* bcrypt 版本
*/
public enum BCryptVersion {
$2A("$2a"),
$2Y("$2y"),
$2B("$2b");
private final String version;
BCryptVersion(String version) {
this.version = version;
}
public String getVersion() {
return this.version;
}
}
}
其他加密器
Argon2PasswordEncoder
Argon2PasswordEncoder 实现使用 Argon2 算法来散列密码。Argon2 是密码哈希竞赛的获胜者。为了打败自定义硬件上的密码破解,Argon2 是一种需要大量内存的故意慢速算法。与其他自适应单向函数一样,应该将其调整为需要大约 1 秒来验证系统上的密码。
SCryptPasswordEncoder
SCryptPasswordEncoder 实现使用 scrypt 算法来散列密码。为了在自定义硬件上破解密码,scrypt 是一种需要大量内存的故意慢速算法。与其他自适应单向函数一样,应该将其调整为需要大约 1 秒来验证系统上的密码。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 蝶梦庄生!
评论