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

背景

抱著什么都不懂的情況下去學(xué)習(xí)使用spring boot 2.x集成oauth2功能,所有的參數(shù)后臺(tái)直接明文配置,結(jié)果發(fā)現(xiàn)報(bào)錯(cuò)了,怎么都無法返回正確的token,Full authentication is required to access this resource
通過后臺(tái)日志報(bào)錯(cuò)內(nèi)容是: There is no PasswordEncoder mapped for the id null

尋根

通過日志,找到了報(bào)錯(cuò)的這個(gè)類 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 + "\"");
        }
    }
}

這個(gè)類是DelegatingPasswordEncoder的一個(gè)內(nèi)部類,通過查看代碼發(fā)現(xiàn)有個(gè)屬性叫做defaultPasswordEncoderForMatches說明這個(gè)內(nèi)部類是一個(gè)默認(rèn)的密碼加密方式,但是從matches這個(gè)方法可以看出目標(biāo)是獲取{}一對(duì)大括號(hào)之間的字符串,如果找不到就會(huì)報(bào)錯(cuò),這說明所有的密碼的格式必然是要{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);
}

繼續(xù)從這個(gè)內(nèi)部類找,可以看到調(diào)用內(nèi)部類的方法位于


    // 這個(gè)matches方式就是進(jìn)行密碼校驗(yàn)的
    @Override
    public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
        // 從字段名字很容易理解
        // rawPassword 原始密碼:也就是用戶請(qǐng)求的密碼
        // prefixEncodedPassword 帶有前綴的加密算法加密之后的密碼
        if (rawPassword == null && prefixEncodedPassword == null) {
            return true;
        }
        // 獲取密碼前綴加密算法
        String id = extractId(prefixEncodedPassword);
        // 從一個(gè)加密算法的map種獲取加密具體的加密算法對(duì)象
        PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
        if (delegate == null) {
            // 如果獲取不到就會(huì)使用默認(rèn)的密碼加密算法,實(shí)際是一定會(huì)拋出錯(cuò)誤的
            // 這個(gè)內(nèi)部類里面再次調(diào)用extraId的目的只是為了獲取一下錯(cuò)誤的前綴而已
            return this.defaultPasswordEncoderForMatches
                .matches(rawPassword, prefixEncodedPassword);
        }
        String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
        return delegate.matches(rawPassword, encodedPassword);
    }

通過以上代碼可以看到具體的加密算法位于this.idToPasswordEncoder,這是一個(gè)map,通過DelegatingPasswordEncoder的構(gòu)造方法進(jìn)行初始化的,查看創(chuàng)建該構(gòu)造函數(shù)追蹤到PasswordEncoderFactories,只有一個(gè)方法

    // 這個(gè)初始化就顯示了所有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對(duì)密碼的處理,以后就能夠很熟悉了

存儲(chǔ)的密碼格式就是 {xxx}yyyyyyyy 的方式了
比如:

123456 -> 存儲(chǔ)為: {MD5}e10adc3949ba59abbe56e057f20f883e
123456 -> 存儲(chǔ)為:{bcrypt}2a10$cDzOYM.AnjxRKyAwQ8LYR.4tJ3WlQKrC4oeus0NfqQsQfjG0jBiRG

在調(diào)試跟進(jìn)源碼的過程中,發(fā)現(xiàn)oauth2的clientSecret 以及密碼模式的password都是走的同一個(gè)邏輯校驗(yàn),這也讓我更容易理解客戶端/自然人都可以作為資源所有者這個(gè)概念。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容