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

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

//a
// 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();
    }

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


原文始发于微信公众号(步尔斯特):【微服务|Spring Security⑬】spring security认证流程之PasswordEncoder

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/48002.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!