版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andy_zhang2007/article/details/84836362
概述
PasswordEncoderFactories
是Spring Security
创建DelegatingPasswordEncoder
对象的工厂类。该工厂所创建的DelegatingPasswordEncoder
缺省使用bcrypt
用于加密,并且能够用于匹配以下几种密码类型 :
- ldap
- MD4
- MD5
- noop (明文密码)
- pbkdf2
- scrypt
- SHA-1
- SHA-256
- sha256
源代码解析
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.crypto.factory;
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 java.util.HashMap;
import java.util.Map;
/**
* Used for creating {@link PasswordEncoder} instances
* @author Rob Winch
* @since 5.0
*/
public class PasswordEncoderFactories {
/**
* 创建 DelegatingPasswordEncoder 实例的工厂方法
* @return the PasswordEncoder to use
*/
@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";// 指定加密密码要使用的PasswordEncoder
// 将Spring Security提供的所有PasswordEncoder实现都包装到所创建的DelegatingPasswordEncoder
// 中,当用于匹配密码时,密码密文的格式是 : "{encoderId}xxxxxxx",DelegatingPasswordEncoder
// 会解析出密码中的"encoderId"从下面的encoders中找到相应的PasswordEncoder去检验输入的密码和
// 密码密文中的"xxxxxxx"是否匹配
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());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
private PasswordEncoderFactories() {}
}
例子演示该工厂类
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
public class PasswordEncoderFactoriesTest {
public static void main(String[] args) {
// 返回的encoder实现类实际上是 DelegatingPasswordEncoder , 它其实是一个 PasswordEncoder 代理,
// 代理了其他一组 PasswordEncoder
// DelegatingPasswordEncoder 用于密码匹配的密码密文必须符合格式 : "{encoderId}xxxxxxx",
// 它所代理的某个 PasswordEncoder 所能接收的密码密文应该是上面例子密码中的 "xxxxxxx" 部分
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
{
// 测试1:缺省用于加密的是 BCryptPasswordEncoder
System.out.println("测试1:缺省用于加密的是 BCryptPasswordEncoder");
final String passwordPlainText = "passw0rdIsTiger";
final String passwordCypher = encoder.encode(passwordPlainText);
System.out.printf("密码明文是 : %s\n", passwordPlainText);
System.out.printf("密码密文是 : %s\n", passwordCypher);
final String expectedPrefix = "{bcrypt}";
System.out.printf("密码密文前缀是 %s : %s\n", expectedPrefix, passwordCypher.startsWith(expectedPrefix));
final boolean match = encoder.matches(passwordPlainText, passwordCypher);
System.out.printf("密码密文和密码明文匹配 : %s\n", match);
}
{
// 测试2:工厂产生的 PasswordEncoder 会根据密码密文encoderId前缀对应的PasswordEncoder进行密码匹配
System.out.println("测试2:工厂产生的 PasswordEncoder 会根据密码密文encoderId前缀对应的PasswordEncoder进行密码匹配");
final String passwordPlainText = "password";
String cypher1 = "{noop}password";
String cypher2 = "{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc";
String cypher3 = "{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=";
String cypher4 = "{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0";
System.out.printf("密码明文 : %s\n", passwordPlainText);
System.out.printf("密码密文1 : %s\n", cypher1);
System.out.printf("密码密文2 : %s\n", cypher2);
System.out.printf("密码密文3 : %s\n", cypher3);
System.out.printf("密码密文4 : %s\n", cypher4);
System.out.printf("密码密文1和密码明文匹配 : %s\n", encoder.matches(passwordPlainText, cypher1));
System.out.printf("密码密文2和密码明文匹配 : %s\n", encoder.matches(passwordPlainText, cypher2));
System.out.printf("密码密文3和密码明文匹配 : %s\n", encoder.matches(passwordPlainText, cypher3));
System.out.printf("密码密文4和密码明文匹配 : %s\n", encoder.matches(passwordPlainText, cypher4));
}
}
}
该例子代码的控制台输出
测试1:缺省用于加密的是 BCryptPasswordEncoder
密码明文是 : passw0rdIsTiger
密码密文是 : {bcrypt}$2a$10$7TVRqzLhYecMy/A03MTHpuwnqmg2yumhxwEc3D3pRoS6CAq9Gbsou
密码密文前缀是 {bcrypt} : true
密码密文和密码明文匹配 : true
测试2:工厂产生的 PasswordEncoder 会根据密码密文encoderId前缀对应的PasswordEncoder进行密码匹配
密码明文 : password
密码密文1 : {noop}password
密码密文2 : {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
密码密文3 : {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
密码密文4 : {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
密码密文1和密码明文匹配 : true
密码密文2和密码明文匹配 : true
密码密文3和密码明文匹配 : true
密码密文4和密码明文匹配 : true