美文网首页
Spring cloud oauth2 研究--密码加密方式

Spring cloud oauth2 研究--密码加密方式

作者: 输入昵称就行 | 来源:发表于2019-05-11 23:48 被阅读0次

背景

抱着什么都不懂的情况下去学习使用spring boot 2.x集成oauth2功能,所有的参数后台直接明文配置,结果发现报错了,怎么都无法返回正确的token,Full authentication is required to access this resource
通过后台日志报错内容是: There is no PasswordEncoder mapped for the id null

寻根

通过日志,找到了报错的这个类 UnmappedIdPasswordEncoder#matches(CharSequence rawPassword,String prefixEncodedPassword)

public class DelegatingPasswordEncoder implements PasswordEncoder {
    
    private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();
    // 省略代码

    private class UnmappedIdPasswordEncoder implements PasswordEncoder {

        @Override
        public String encode(CharSequence rawPassword) {
            throw new UnsupportedOperationException("encode is not supported");
        }

        @Override
        public boolean matches(CharSequence rawPassword,
            String prefixEncodedPassword) {
            String id = extractId(prefixEncodedPassword);
            throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
        }
    }
}

这个类是DelegatingPasswordEncoder的一个内部类,通过查看代码发现有个属性叫做defaultPasswordEncoderForMatches说明这个内部类是一个默认的密码加密方式,但是从matches这个方法可以看出目标是获取{}一对大括号之间的字符串,如果找不到就会报错,这说明所有的密码的格式必然是要{xxx}xxxxx这种方式。

// 获取 {} 之间的字符串
private String extractId(String prefixEncodedPassword) {
    if (prefixEncodedPassword == null) {
        return null;
    }
    int start = prefixEncodedPassword.indexOf(PREFIX);
    if (start != 0) {
        return null;
    }
    int end = prefixEncodedPassword.indexOf(SUFFIX, start);
    if (end < 0) {
        return null;
    }
    return prefixEncodedPassword.substring(start + 1, end);
}

继续从这个内部类找,可以看到调用内部类的方法位于


    // 这个matches方式就是进行密码校验的
    @Override
    public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
        // 从字段名字很容易理解
        // rawPassword 原始密码:也就是用户请求的密码
        // prefixEncodedPassword 带有前缀的加密算法加密之后的密码
        if (rawPassword == null && prefixEncodedPassword == null) {
            return true;
        }
        // 获取密码前缀加密算法
        String id = extractId(prefixEncodedPassword);
        // 从一个加密算法的map种获取加密具体的加密算法对象
        PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
        if (delegate == null) {
            // 如果获取不到就会使用默认的密码加密算法,实际是一定会抛出错误的
            // 这个内部类里面再次调用extraId的目的只是为了获取一下错误的前缀而已
            return this.defaultPasswordEncoderForMatches
                .matches(rawPassword, prefixEncodedPassword);
        }
        String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
        return delegate.matches(rawPassword, encodedPassword);
    }

通过以上代码可以看到具体的加密算法位于this.idToPasswordEncoder,这是一个map,通过DelegatingPasswordEncoder的构造方法进行初始化的,查看创建该构造函数追踪到PasswordEncoderFactories,只有一个方法

    // 这个初始化就显示了所有oauth2的加密方式,比如常用的MD5,bcrypt等,
    // ==noop==代表不加密
    public static PasswordEncoder createDelegatingPasswordEncoder() {
        String encodingId = "bcrypt";
        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);
    }

至此oauth对密码的处理,以后就能够很熟悉了

存储的密码格式就是 {xxx}yyyyyyyy 的方式了
比如:

123456 -> 存储为: {MD5}e10adc3949ba59abbe56e057f20f883e
123456 -> 存储为:{bcrypt}2a10$cDzOYM.AnjxRKyAwQ8LYR.4tJ3WlQKrC4oeus0NfqQsQfjG0jBiRG

在调试跟进源码的过程中,发现oauth2的clientSecret 以及密码模式的password都是走的同一个逻辑校验,这也让我更容易理解客户端/自然人都可以作为资源所有者这个概念。

相关文章

网友评论

      本文标题:Spring cloud oauth2 研究--密码加密方式

      本文链接:https://www.haomeiwen.com/subject/bkhqaqtx.html