1. 程式人生 > >Spring Security Core 5.1.2 原始碼解析 -- PasswordEncoderFactories

Spring Security Core 5.1.2 原始碼解析 -- PasswordEncoderFactories

概述

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