package com.wholesmart.core.security.common;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
/**
* 使用scrypt加密需要引入bcprov-ext-jdk15on.jar
*
* <pre>
* 初始化已经支持的编码器:
* 1、"noop"-->{noop}123456
* 2、"bcrypt"-->{bcrypt}$2a$10$fHZZyvq79d/1uaMZM1rBmOXuwlql8sFsNNL3g3LEvRcsJyGHlxtfq
* 3、"scrypt"-->{scrypt}$e0801$VZE2uFeSuNo4osWGMfqm3S/2UHbFpl26oKRyM6pn0HhhKbnjETeBaB8xsKFVS/AnMgmwUt2r89nTDlH8hfNaZQ==$bbf8t4aN1F+cpYeyA6XHSqnoMNiQd+dI3SXPLYRQ3+U=
* 4、"pbkdf2"-->{pbkdf2}b204a92c31c540a94c6b02588949a993125db15eea3e78e605b21490e2cc013e42a843d320c74cc2
* 5、"argon2"-->{argon2}$argon2id$v=19$m=4096,t=3,p=1$8y+6T6VucGtwrKaVfG0W+w$QaAx0IsQAwlJdSqy2meqgKDRuCmUYcV0C/nnBxHofpY
* </pre>
*
*/
public class CustomDelegatingPasswordEncoder {
/**
* 存入所有被支持的编码器
*/
private final Map<String, PasswordEncoder> passwordEncoderMap;
/**
* 默认的编码器
*/
private PasswordEncoder defaultEncoderForEncode;
/**
* 默认的编码器标识
*/
private String defaultIdForEncode = "bcrypt";
/**
* 编码器标识的分隔符
*/
private static final String PREFIX = "{";
private static final String SUFFIX = "}";
/**
* 没有查找到编码器时时使用的默认编码器
*/
private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();
public CustomDelegatingPasswordEncoder() {
this.passwordEncoderMap = initializedPasswordEncoderMap();
this.defaultEncoderForEncode = passwordEncoderMap.get(defaultIdForEncode);
}
/**
* 默认初始化的编码器
*
* @return
*/
private Map<String, PasswordEncoder> initializedPasswordEncoderMap() {
PasswordEncoder bcrypt = new BCryptPasswordEncoder();
PasswordEncoder scrypt = new SCryptPasswordEncoder();
PasswordEncoder pbkdf2 = new Pbkdf2PasswordEncoder();
PasswordEncoder argon2 = new Argon2PasswordEncoder();
PasswordEncoder noop = new NoopPasswordEncoder();
Map<String, PasswordEncoder> initializedPasswordEncoderMap = new HashMap<String, PasswordEncoder>();
initializedPasswordEncoderMap.put("bcrypt", bcrypt);
initializedPasswordEncoderMap.put("scrypt", scrypt);
initializedPasswordEncoderMap.put("pbkdf2", pbkdf2);
initializedPasswordEncoderMap.put("argon2", argon2);
initializedPasswordEncoderMap.put("noop", noop);
return initializedPasswordEncoderMap;
}
/**
* 添加额外的编码器
*
* @param idForEncode 编码器标识
* @param passwordEncoder 编码器
*/
public CustomDelegatingPasswordEncoder addMorePasswordEncoder(String idForEncode, PasswordEncoder passwordEncoder) {
if (idForEncode == null)
throw new IllegalArgumentException("idForEncode cannot be null");
if (passwordEncoder == null) {
throw new IllegalArgumentException("passwordEncoder cannot be null");
}
this.passwordEncoderMap.put(idForEncode, passwordEncoder);
return this;
}
/**
* 修改默认编码器
*
* @param idForEncode 编码器标识
*/
public CustomDelegatingPasswordEncoder setDefaultIdForEncode(String idForEncode) {
if (idForEncode == null)
throw new IllegalArgumentException("idForEncode cannot be null");
if (!passwordEncoderMap.containsKey(idForEncode))
throw new IllegalArgumentException("idForEncode not be existed");
if (passwordEncoderMap.get(idForEncode) == null)
throw new IllegalArgumentException("idForEncode mapped PasswordEncoder cannot be null");
this.defaultIdForEncode = idForEncode;
this.defaultEncoderForEncode = passwordEncoderMap.get(idForEncode);
return this;
}
/**
* 如果在passwordEncoderMap没有指定标识的编码器时使用这个方法设置的编码器
*
* @param defaultPasswordEncoderForMatches
*/
public void setDefaultPasswordEncoderForMatches(PasswordEncoder defaultPasswordEncoderForMatches) {
if (defaultPasswordEncoderForMatches == null) {
throw new IllegalArgumentException("defaultPasswordEncoderForMatches cannot be null");
}
this.defaultPasswordEncoderForMatches = defaultPasswordEncoderForMatches;
}
/**
* 使用配置的默认编码器编码
*
* @param rawPassword
* @return
*/
public String encode(String rawPassword) {
return PREFIX + this.defaultIdForEncode + SUFFIX + this.defaultEncoderForEncode.encode(rawPassword);
}
/**
* 使用指导标识的编码器编码
*
* @param rawPassword 明文
* @param idForEncode 编码器标识
* @return
*/
public String encode(String rawPassword, String idForEncode) {
PasswordEncoder delegate = passwordEncoderMap.get(idForEncode);
if (delegate == null) {
return this.defaultPasswordEncoderForMatches.encode(rawPassword);
}
return PREFIX + this.defaultIdForEncode + SUFFIX + delegate.encode(rawPassword);
}
/**
* 匹配密码
*
* @return
*/
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
if (rawPassword == null && prefixEncodedPassword == null) {
return true;
}
String encodingId = extractPrefix(prefixEncodedPassword);
// 根据编码标识获取编码器
PasswordEncoder delegate = this.passwordEncoderMap.get(encodingId);
// 如果没有支持的编码器,使用默认编码器处理
if (delegate == null) {
return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);
}
// 如果查找到指定编码器,获取不带前缀的标识然后编码
String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
return delegate.matches(rawPassword, encodedPassword);
}
/**
* 获取标识
*
* @param prefixEncodedPassword 带有标识前缀的密码
* @return
*/
private String extractPrefix(String prefixEncodedPassword) {
if (prefixEncodedPassword == null) {
return null;
}
int start = prefixEncodedPassword.indexOf(PREFIX);
if (start != 0) {
return null;
}
int end = prefixEncodedPassword.indexOf(SUFFIX, start);
if (end < 0) {
return null;
}
return prefixEncodedPassword.substring(start + 1, end);
}
/**
* 获取不带前缀的密文
*
* @param prefixEncodedPassword
* @return
*/
private String extractEncodedPassword(String prefixEncodedPassword) {
int start = prefixEncodedPassword.indexOf(SUFFIX);
return prefixEncodedPassword.substring(start + 1);
}
/**
* 不加密的编码器
*
* @author dyw
* @data 2020年9月10日
*/
private class NoopPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
}
/**
* 没有指定标识的编码器时的默认编码器
*
* @author dyw
* @data 2020年9月10日
*/
private class UnmappedIdPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
throw new UnsupportedOperationException("encode is not supported");
}
@Override
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
String id = extractPrefix(prefixEncodedPassword);
throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
}
}
}
恰如初见,一切美好。怀着一颗赤子之心,未来的每一步才会更坚定,曾经的每一段缘才会更珍惜。