Java的密码加密解密器------CustomDelegatingPasswordEncoder密码编码器

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 + "\"");
		}
	}
}

恰如初见,一切美好。怀着一颗赤子之心,未来的每一步才会更坚定,曾经的每一段缘才会更珍惜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豢龙先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值