之前写了一篇文章关于MD5加密的文章 MD5+盐,但是如果知道加盐的规则,很容易进行反向破解,也不安全。
因此引入BCrypt加密算法进行加密
1.为什么BCrypt比MD5+盐更加安全?
BCrpyt也是输入的字符串+盐,但是与MD5+盐的主要区别是:每次加的盐不同,导致每次生成的结果也不相同。无法比对!
2.使用Bcrypt(注解开发)
结合SpringSecurity提供的BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强哈希方法来加密密码。每次加密后结果不同。
2.1引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
2.2书写配置类
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests() //所有security全注解配置实现的开端,表示开始说明需要的权限 第一部分:拦截的路径 第二部分:访问该路径需要的权限.antMatchers("/**").permitAll() //antMatchers 拦截路径 permitAll任何权限都可以进行访问 .hasAnyAuthority 特殊权限才可访问.anyRequest().authenticated() //anyRequest 任何的请求 authenticated 认证后才可访问.and().csrf().disable(); //必要参数(固定写法) 使csrf拦截失效}
}
2.3将BCryptPasswordEncoder当做bean对象,方便注入
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){return new BCryptPasswordEncoder();
}
3.BCrypt的使用
3.1加密使用:
(在使用处注入BCryptPasswordEncoder)
String encodePassword = encoder.encode(admin.getPassword());
encode源码:(关键在于genSalt()导致盐不同)
public String encode(CharSequence rawPassword) {String salt;if (this.strength > 0) {if (this.random != null) {salt = BCrypt.gensalt(this.strength, this.random);} else {salt = BCrypt.gensalt(this.strength);}} else {salt = BCrypt.gensalt();}return BCrypt.hashpw(rawPassword.toString(), salt);}
主要是调用Bcrypt.hashpw(string,salt); //salt是随机的,不固定
贴出genSalt()源码:
public static String gensalt(int log_rounds, SecureRandom random) {if (log_rounds >= 4 && log_rounds <= 31) {StringBuilder rs = new StringBuilder();byte[] rnd = new byte[16];random.nextBytes(rnd);rs.append("$2a$");if (log_rounds < 10) {rs.append("0");}rs.append(log_rounds);rs.append("$");encode_base64(rnd, rnd.length, rs);return rs.toString();} else {throw new IllegalArgumentException("Bad number of rounds");}}public static String gensalt(int log_rounds) {return gensalt(log_rounds, new SecureRandom());}
3.2对比并返回对比结果
Boolean isEqual = encoder.matches(admin.getPassword(),encodePassword);
源码为:
public boolean matches(CharSequence rawPassword, String encodedPassword) {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;}}
主要是使用了BCrypt.checkpw(string,encoderString);//对比两个字符串是否相等,
再往下追踪源码:
public static boolean checkpw(String plaintext, String hashed) {return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));}static boolean equalsNoEarlyReturn(String a, String b) {char[] caa = a.toCharArray();char[] cab = b.toCharArray();if (caa.length != cab.length) {return false;} else {byte ret = 0;for(int i = 0; i < caa.length; ++i) {ret = (byte)(ret | caa[i] ^ cab[i]);}return ret == 0;}}
首先对传入的string,按照encoderString加密方式进行加密。
然后按照位比较,一致返回true,反之返回false