【微服务|Spring Security⑬】spring security认证流程之PasswordEncoder

DaoAuthenticationProvide以证处理器通过UserDetailsService获取到UserDetails后,它是如何与请求 Authentication中的密码做对比呢?在这里Spring Security为了适应多种多样的加密类型,又做了抽象,DaoAuthenticationProvider通过 PasswordEncoder接口的matches方法进行密码的对比,而具体的密码对比细节取决于实现:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.crypto.password;

public interface PasswordEncoder {
    
    
    String encode(CharSequence var1);

    boolean matches(CharSequence var1, String var2);

    default boolean upgradeEncoding(String encodedPassword) {
    
    
        return false;
    }
}

而Spring Security提供很多内置的PasswordEncoder,能够开箱即用,使用某种PasswordEncoder只需要进行如下声明即可,如下:

@Bean
public PasswordEncoder passwordEncoder() {
    
    
	return NoOpPasswordEncoder.getInstance();
}

NoOpPasswordEncoder采用字符串匹配方法,不对密码进行加密比较处理,密码比较流程如下:

1、用户输入密码(明文)
2、DaoAuthenticationProvider获取UserDetails (其中存储了用户的正确密码)
3、DaoAuthenticationProvider使用PasswordEncoder对输入的密码和正确的密码进行校验,密码一致则校验通过,否则校验失败。

NoOpPasswordEncode啲校验规则拿输入的密码和UserDetails中的正确密码进行字符串比较,字符串内容一致 则校验通过,否则校验失败。

实际项目中推荐使用BCryptPasswordEncoder, Pbkdf2PasswordEncoder,SCryptPasswordEncoder等。

使用BCryptPasswordEncoder

1、 配置BCryptPasswordEncoder 在安全配置类中定义:

@Bean 
public PasswordEncoder passwordEncoder() {
      
       
	return new BCryptPasswordEncoder(); 
}

测试发现认证失败,提示:Encoded password does not look like BCrypt。

 public boolean matches(CharSequence rawPassword, String encodedPassword) {
    
    
        if (rawPassword == null) {
    
    
            throw new IllegalArgumentException("rawPassword cannot be null");
        } else if (encodedPassword != null && encodedPassword.length() != 0) {
    
    
            if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
    
    
                this.logger.warn("Encoded password does not look like BCrypt");
                return false;
            } else {
    
    
                return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
            }
        } else {
    
    
            this.logger.warn("Empty encoded password");
            return false;
        }
    }

原因:

由于UserDetails中存储的是原始密码(比如:123 ),它不是BCrypt格式。 跟踪DaoAuthenticationProvider第33行代码查看userDetails中的内容,跟踪第38行代码查看 Password Encoder的类型。

 protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    
    
        if (authentication.getCredentials() == null) {
    
    //33行
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
    
    
            String presentedPassword = authentication.getCredentials().toString();
            if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
    
    //38行
                this.logger.debug("Failed to authenticate since password does not match stored value");
                throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
        }
    }

测试BCrypt
通过下边的代码测试BCrypt加密及校验的方法

添加依赖:

<dependency>
	<groupld>org.springframework.boot</groupld>
	<artifactld>spring-boot-starter-test</artifactld>
	<scope>test</scope>
</dependency>

编写测试方法:

package com.uncle.seciruty.springboot.util;

import org.springframework.security.crypto.bcrypt.BCrypt;

/**
 * @program: spring-boot-security
 * @description:
 * @author: 步尔斯特
 * @create: 2021-08-06 22:12
 */
public class BCryptUtil {
    
    
    public static void main(String[] args) {
    
    
        String hashpw = BCrypt.hashpw("456", BCrypt.gensalt());
        System.out.println(hashpw);

        boolean checkpw = BCrypt.checkpw("456","$2a$10$bcJXXryMCxXtkxRkG1UekOkOe0BqxiqOYKJzGni64jnyWAD15wmDy");
        System.out.println(checkpw);

        //123 -> $2a$10$8iHn2TEvyzkUgO2np9glzufe.wtRyjA5u3xfvs/D.9FCzm1XvCAGm
        //456

    }
}

修改安全配置类

将UserDetails中的原始密码修改为BCrypt格式:

    //密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }

实际项目中存储在数据库中的密码并不是原始密码,都是经过加密处理的密码。

猜你喜欢

转载自blog.csdn.net/CSDN_SAVIOR/article/details/125683287