SpringSecurity多种密码策略

Spring Security 版本 5.7.3

PasswordEncoder 生成类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import com.maxzhao.security.crypto.BootPasswordProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
@AutoConfigureAfter(BootPasswordProperties.class)
public class PasswordEncoderFactory {
@Resource
private BootPasswordProperties bootPasswordProperties;
/**
* 加密方式的实现
*/
private final Map<String, PasswordEncoder> idToPasswordEncoder = new HashMap<>(16);


/**
* 注册bean
*
* @return PasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return getDelegatingPasswordEncoder();
}

/**
* 自定义加密密码
*
* @param idForEncode 加密密码的方式
* @param rawPassword 密码
* @return 加密后的密码
*/
public String encode(String idForEncode, CharSequence rawPassword) {
if (idForEncode == null) {
throw new IllegalArgumentException("idForEncode cannot be null");
}
PasswordEncoder passwordEncoder = idToPasswordEncoder.get(idForEncode);
if (passwordEncoder == null) {
throw new IllegalArgumentException(idForEncode + " PasswordEncoder cannot be null");
}
return passwordEncoder.encode(rawPassword);
}

/**
* 构建 多密码策略
*
* @return DelegatingPasswordEncoder
*/
public PasswordEncoder getDelegatingPasswordEncoder() {
String defaultEncoderId = bootPasswordProperties.getDefaultEncoderId();
if (!StringUtils.hasText(defaultEncoderId)) {
log.warn("默认密码加密方式不存在,已使用默认配置 {}", BootPasswordProperties.DEFAULT_ENCODER_ID);
defaultEncoderId = BootPasswordProperties.DEFAULT_ENCODER_ID;
}
if (idToPasswordEncoder.isEmpty()) {
Map<String, String> encoder = bootPasswordProperties.getEncoder();
if (encoder == null) {
encoder = new HashMap<>(0);
}
encoder.forEach((key, value) -> {
try {
Class<?> com = Class.forName(value);
Object o = com.newInstance();
if (o instanceof PasswordEncoder) {
idToPasswordEncoder.put(key, (PasswordEncoder) o);
} else {
log.error("加载自定义 {} 失败:{} 需要实现 PasswordEncoder 接口 ", key, value);
}
} catch (ClassNotFoundException e) {
log.error("加载自定义 {} 失败:PasswordEncoder[{}] ", key, value);
throw new RuntimeException(e);
} catch (InstantiationException | IllegalAccessException e) {
log.error("实例化 {} 失败:PasswordEncoder[{}] ", key, value);
throw new RuntimeException(e);
}
});
idToPasswordEncoder.put("bcrypt", new BCryptPasswordEncoder());
idToPasswordEncoder.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
idToPasswordEncoder.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
idToPasswordEncoder.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
idToPasswordEncoder.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
idToPasswordEncoder.put("pbkdf2", new Pbkdf2PasswordEncoder());
idToPasswordEncoder.put("scrypt", new SCryptPasswordEncoder());
idToPasswordEncoder.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
idToPasswordEncoder.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
idToPasswordEncoder.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
idToPasswordEncoder.put("argon2", new Argon2PasswordEncoder());
}
/*Tip[DelegatingPasswordEncoder]: 使用当前密码策略,需要考虑历史密码的迁移*/
return new DelegatingPasswordEncoder(defaultEncoderId, idToPasswordEncoder);
}
}

BootPasswordProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Data
@Configuration
@ConfigurationProperties(prefix = "boot.security.crypto")
public class BootPasswordProperties {
/**
* 默认密码加密方式
*/
public static final String DEFAULT_ENCODER_ID = "bcrypt";
/**
* 默认密码加密方式 <br>
* 切换密码加密方式,修改当前配置
*/
private String defaultEncoderId = DEFAULT_ENCODER_ID;
/**
* 密码加密方式<br>
* key 名称,value 类的全路径 <br>
* 示例
* boot:
* security:
* crypto:
* defaultEncoderId: sm2
* encoder:
* - key: sm2
* value: com.maxzhao.security.crypto.password.Sm2PasswordEncoder
*/
private Map<String, String> encoder = new HashMap<>(4);

}

yml 配置自定义密码加密方式

1
2
3
4
5
6
boot:
security:
crypto:
default-encoder-id:
encoder:
sm2: com.maxzhao.security.crypto.password.Sm2PasswordEncoder

Sm2PasswordEncoder 需要实现 org.springframework.security.crypto.password.PasswordEncoder 接口

SpringSecurity 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Slf4j
@Configuration
public class AuthorizationServerSecurityConfiguration {
/**
* 密码加密创建工具
*/
@Resource
private UserDetailsService userDetailsService;

/**
* 密码加密创建工具
*/
@Resource
private PasswordEncoder passwordEncoder;
@Bean
@Order(2)
public SecurityFilterChain standardSecurityFilterChain(HttpSecurity http, UserCache userCache) throws Exception {
AuthenticationManagerBuilder authenticationBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
/*自定义 DaoAuthenticationProvider */
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
/*配置缓存*/
provider.setUserCache(userCache);
authenticationBuilder.authenticationProvider(provider);
http.authenticationManager(authenticationBuilder.build());
/* ***************** */
return http.build();
}

}

本文地址: https://github.com/maxzhao-it/blog/post/737197b3/