2023年7月27日发(作者:)
SpringsecurityBCryptPasswordEncoder密码验证原理详解⼀、加密算法和hash算法的区别加密算法是⼀种可逆的算法,基本过程就是对原来为明⽂的⽂件或数据按某种算法进⾏处理,使其成为不可读的⼀段代码为“密⽂”,但在⽤相应的密钥进⾏操作之后就可以得到原来的内容 。哈希算法是⼀种不可逆的算法,是把任意长度的输⼊通过散列算法变换成固定长度的输出,输出就是散列值,不同的输⼊可能会散列成相同的输出,所以不可能从散列值来确定唯⼀的输⼊值。⼆、源码解析BCryptPasswordEncoder类实现了PasswordEncoder接⼝,这个接⼝中定义了两个⽅法public interface PasswordEncoder { String encode(CharSequence rawPassword); boolean matches(CharSequence rawPassword, String encodedPassword);}其中encode(...)是对字符串进⾏加密的⽅法,matches使⽤来校验传⼊的明⽂密码rawPassword是否和加密密码encodedPassword相匹配的⽅法。即对密码进⾏加密时调⽤encode,登录认证时调⽤matches下⾯我们来看下BCryptPasswordEncoder类中这两个⽅法的具体实现1. encode⽅法public String encode(CharSequence rawPassword) { String salt; if (strength > 0) { if (random != null) { salt = t(strength, random); } else { salt = t(strength); } } else { salt = t(); } return (ng(), salt);}可以看到,这个⽅法中先基于某种规则得到了⼀个盐值,然后在调⽤⽅法,传⼊明⽂密码和盐值salt。所以我们再看下⽅法中做了什么2. ⽅法public static String hashpw(String password, String salt) throws IllegalArgumentException { BCrypt B; String real_salt; byte passwordb[], saltb[], hashed[]; char minor = (char) 0; int rounds, off = 0; StringBuilder rs = new StringBuilder(); if (salt == null) { throw new IllegalArgumentException("salt cannot be null"); } int saltLength = (); if (saltLength < 28) { throw new IllegalArgumentException("Invalid salt"); } if ((0) != '$' || (1) != '2') { throw new IllegalArgumentException("Invalid salt version"); } if ((2) == '$') { off = 3; } else { minor = (2); if (minor != 'a' || (3) != '$') { throw new IllegalArgumentException("Invalid salt revision"); } off = 4; } if (saltLength - off < 25) { throw new IllegalArgumentException("Invalid salt"); } // Extract number of rounds if ((off + 2) > '$') { throw new IllegalArgumentException("Missing salt rounds"); } rounds = nt(ing(off, off + 2)); real_salt = ing(off + 3, off + 25); try { passwordb = (password + (minor >= 'a' ? "000" : "")).getBytes("UTF-8"); } catch (UnsupportedEncodingException uee) { throw new AssertionError("UTF-8 is not supported"); } saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); B = new BCrypt(); hashed = _raw(passwordb, saltb, rounds); ("$2"); if (minor >= 'a') { (minor); } ("$"); if (rounds < 10) { ("0"); } (rounds); ("$"); encode_base64(saltb, , rs); encode_base64(hashed, bf_crypt_ * 4 - 1, rs); return ng(); }可以看到,这个⽅法中先根据传⼊的盐值salt,然后基于某种规则从salt得到real_salt,后续的操作都是⽤这个real_salt来进⾏,最终得到加密字符串。所以这⾥有⼀个重点:传⼊的盐值salt并不是最终⽤来加密的盐,⽅法中通过salt得到了real_salt,记住这⼀点,因为后边的匹配⽅法matches中要⽤到这⼀点。3. matches⽅法matches⽅法⽤来判断⼀个明⽂是否和⼀个加密字符串对应。public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || () == 0) { ("Empty encoded password"); return false; } if (!BCRYPT_r(encodedPassword).matches()) { ("Encoded password does not look like BCrypt"); return false; } return w(ng(), encodedPassword);}这个⽅法中先对密⽂字符串进⾏了⼀些校验,如果不符合规则直接返回不匹配,然后调⽤校验⽅法w,第⼀个参数是明⽂,第⼆个参数是加密后的字符串。public static boolean checkpw(String plaintext, String hashed) { return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));}static boolean equalsNoEarlyReturn(String a, String b) { char[] caa = Array(); char[] cab = Array(); if ( != ) { return false; } byte ret = 0; for (int i = 0; i < ; i++) { ret |= caa[i] ^ cab[i]; } return ret == 0;}注意 equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed))这⾥,第⼀个参数是加密后的字符串,⽽第⼆个参数是⽤刚才提过的hashpw⽅法对明⽂字符串进⾏加密。hashpw(plaintext, hashed)第⼀个参数是明⽂,第⼆个参数是加密字符串,但是在这⾥是作为盐值salt传⼊的,所以就⽤到了刚才说的 hashpw 内部通过传⼊的salt得到real_salt,这样就保证了对现在要校验的明⽂的加密和得到已有密⽂的加密⽤的是同样的加密策略,算法和盐值都相同,这样如果新产⽣的密⽂和原来的密⽂相同,则这两个密⽂对应的明⽂字符串就是相等的。这也说明了加密时使⽤的盐值被写在了最终⽣成的加密字符串中。三、总结BCryptPasswordEncoder使⽤哈希算法+随机盐来对字符串加密。因为哈希是⼀种不可逆算法,所以密码认证时需要使⽤相同的算法+盐值来对待校验的明⽂进⾏加密,然后⽐较这两个密⽂来进⾏验证。BCryptPasswordEncoder在加密时通过从传⼊的salt中获取real_salt⽤来加密,保证了这⼀点。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1690464448a353179.html
评论列表(0条)