Erlo

SpringSecurity5(3-密码解析器)

2025-03-12 22:29:27 发布   1 浏览  
页面报错/反馈
收藏 点赞

PasswordEncoder

public interface PasswordEncoder {
    //加密
    String encode(CharSequence var1);
    //比较密码
    boolean matches(CharSequence var1, String var2);
	
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}

工作流程

WebSecurityConfigurerAdapter 初始化密码解析器时,如果没有自定义 Bean 的话,会默认初始化 DelegatingPasswordEncoder

DaoAuthenticationProvider 在 additionalAuthenticationChecks 方法中会获取 Spring 容器中的 PasswordEncoder 来对用户输入的密码进行比较

BCryptPasswordEncoder 密码匹配流程

public class BCryptPasswordEncoder implements PasswordEncoder {
    private Pattern BCRYPT_PATTERN = Pattern
            .compile("\A\$2(a|y|b)?\$(\d\d)\$[./0-9A-Za-z]{53}");
    private final Log logger = LogFactory.getLog(getClass());

    private final int strength;
    private final BCryptVersion version;

    private final SecureRandom random;
    
    /**
     * 参数:原密码
     */
    public String encode(CharSequence rawPassword) {
        if (rawPassword == null) {
            throw new IllegalArgumentException("rawPassword cannot be null");
        }

        String salt;
        if (random != null) {
            salt = BCrypt.gensalt(version.getVersion(), strength, random);
        } else {
            salt = BCrypt.gensalt(version.getVersion(), strength);
        }
        return BCrypt.hashpw(rawPassword.toString(), salt);
    }


    /**
     * 参数一:原密码,
     * 参数二:加密后保存在数据库的密码
     */
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        if (encodedPassword == null || encodedPassword.length() == 0) {
            logger.warn("Empty encoded password");
            return false;
        }

        if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
            logger.warn("Encoded password does not look like BCrypt");
            return false;
        }

        return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
    }
}

从数据库得到的“密码”(参数: salt )进行一系列校验(长度校验等)并截取“密码”中相应的密码盐,利用这个密码盐进行同样的一系列计算 Hash 操作和 Base64 编码拼接一些标识符生成所谓的“密码”,最后 equalsNoEarlyReturn 方法对同一个密码盐生成的两个“密码”进行匹配

  1. 每次使用 BCryptPasswordEncoder 编码同一个密码都是不一样的,因为用到的随机密码盐每次都是不一样的,同一个密码和不同的密码盐组合计算出来的 Hash 值不一样
  2. BCryptPasswordEncoder 编码同一个密码后结果都不一样,怎么进行匹配?因为密码盐是随机生成的,但是可以根据数据库查询出来的“密码”拿到密码盐,同一个密码盐+原密码计算 Hash 结果值是能匹配的
public class BCrypt {
    
    public static String hashpw(String password, String salt) {
        byte passwordb[];
        passwordb = password.getBytes(StandardCharsets.UTF_8);
        return hashpw(passwordb, salt);
    }
    
    public static String hashpw(byte passwordb[], String salt) {
        BCrypt B;
        String real_salt;
        byte saltb[], hashed[];
        char minor = (char) 0;
        int rounds, off;
        StringBuilder rs = new StringBuilder();
        if (salt == null) {
            throw new IllegalArgumentException("salt cannot be null");
        }
        int saltLength = salt.length();
        if (saltLength  '$')
            throw new IllegalArgumentException ("Missing salt rounds");

        if (off == 4 && saltLength = 'a') // add null terminator
            passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);

        B = new BCrypt();
        hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
        rs.append("$2");
        if (minor >= 'a')
            rs.append(minor);
        rs.append("$");
        if (rounds 

自定义密码解析器

public class MyMD5PasswordEncoder implements PasswordEncoder {

    /**
     * 加密
     * @param charSequence  明文字符串
     */
    @Override
    public String encode(CharSequence charSequence) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            return toHexString(digest.digest(charSequence.toString().getBytes()));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 密码校验
     * @param charSequence 明文,页面收集密码
     * @param s 密文 ,数据库中存放密码
     */
    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(encode(charSequence));
    }

    /**
     * @param tmp 转 16 进制字节数组
     * @return 饭回 16 进制字符串
     */
    private String toHexString(byte [] tmp){
        StringBuilder builder = new StringBuilder();
        for (byte b :tmp){
            String s = Integer.toHexString(b & 0xFF);
            if (s.length()==1){
                builder.append("0");
            }
            builder.append(s);
        }
        return builder.toString();
    }
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder(); //自带的
        return new MyMD5PasswordEncoder();
    }
}
@Slf4j
@Component
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        String password = passwordEncoder.encode("123");
        log.info("登录用户:{},密码:{}", s, password);
        return new User(s, password, AuthorityUtilcnblogs.commaSeparatedStringToAuthorityList("admin"));
    }
}

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认