Spring Security Core 5.1.2 源码解析 -- PasswordEncoderFactories

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andy_zhang2007/article/details/84836362

概述

PasswordEncoderFactoriesSpring 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

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/84836362