一.简介
在前面的文章有提到过PasswordEncoder: 为什么密码使用{noop}开头呢?也做出了相应的解释,这篇文章了解PasswordEncoder。
二.PassworderEncoder 详解
2.1主要方法
- String encode(CharSequence rawPassword):密码加密
- boolean matches(CharSequence rawPassword, String encodedPassword):密码匹配
- boolean upgradeEncoding(String encodedPassword): 升级密码,使用新规则来更新旧密码规则
2.2主要实现
- DelegatingPasswordEncoder 加密代理 默认的PassworderEncoder实例
- BCryptPasswordEncoder DelegatingPasswordEncoder 中默认的加密方式
- NoOpPasswordEncoder 不加密
- LazyPasswordEncoder 需要用的时候才初始化
- MessageDigestPasswordEncoder
- Md4PasswordEncoder
2.3默认PasswordEncoder
默认PasswordEncoder的代码如下:
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256",
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
encoders.put("argon2", new Argon2PasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
2.4识别密码类型核心逻辑
识别密码类型核心逻辑的代码如下:
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256",
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
encoders.put("argon2", new Argon2PasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
主要逻辑:
-
如果密码为空,则返回true
-
解析密码ID->{xxxx}
-
根据ID从map中获取PassworderEncoder
-
如果获取不到PassworderEncoder: {xxxx}为空或者不是合法的值,则跑出异常
-
如果获取到,则使用对应的PasswordEncoder进行密码匹配
从这段源码中,我们就知道默认的provider提供的密码处理器逻辑: -
如果IOC容器中有且仅有一个PasswordEncoder,那么就使用IOC中的PassworderEncoder
-
如果没有,则使用DaoAuthenticationProvider 自己提供的,看构造方法逻辑:
三.Passworder实战
3.1验证默认的PassworderEncoder实例
分别使用BCryptPasswordEncoder、Argon2PasswordEncoder加密密码,代码如下:
public static void main(String[] args) {
System.out.println("{pbkdf2}"+new Pbkdf2PasswordEncoder().encode("123456"));
System.out.println("{bcrypt}"+new BCryptPasswordEncoder().encode("123456"));
}
3.2基于内存模式添加用户
代码如下:
@Bean
public UserDetailsService userDetailsService(){
UserDetails noop = User.withUsername("noop").password("{noop}123456").roles("admin").build();
UserDetails bcypt = User.withUsername("bcrypt").password("{bcrypt}$2a$10$MI6ueeZD8uhAbCy1SH2FSuTxkARMc2x6Lzw.x4ax0ybpoXJLIrl8u").roles("admin").build();
UserDetails pbkdf2 = User.withUsername("pbkdf2").password("{pbkdf2}ac487cc5d0df83aa3f4d130b1c94063feb6facfc597266175384b78eb432c382fe3aef332ffaff34").roles("admin").build();
return new InMemoryUserDetailsManager(noop,bcypt,pbkdf2);
}
验证登录
3.3指定PasswordEncoder
注入Bean,代码如下:
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
验证登录
还是基于上面的配置,使用:noop/123456 bcrypt/123456 pbkdf2/123456 发现全部登录失败,截图如下:
因为我们现在指定了PasswordEncoder=BcryptPasswordEncoder,替代了默认的DelegatingPasswordEncoder,所以我们只需要将密码前缀{xxx}去掉就行,但是使用其他加密方式:pbkdf2/123456 和 noop/123456 还是登录不上,因为你使用的是BcryptPasswordEncoder进行密码匹配,所以需要更新密码。
代码如下:
@Bean
public UserDetailsService userDetailsService(){
UserDetails noop = User.withUsername("noop").password("{noop}123456").roles("admin").build();
UserDetails bcypt = User.withUsername("bcrypt").password("$2a$10$MI6ueeZD8uhAbCy1SH2FSuTxkARMc2x6Lzw.x4ax0ybpoXJLIrl8u").roles("admin").build();
UserDetails pbkdf2 = User.withUsername("pbkdf2").password("{pbkdf2}ac487cc5d0df83aa3f4d130b1c94063feb6facfc597266175384b78eb432c382fe3aef332ffaff34").roles("admin").build();
return new InMemoryUserDetailsManager(noop,bcypt,pbkdf2);
}