SpringSecurity04
密码加密
基础密码加密
- SpringSecurity提供的加密接口
public interface PasswordEncoder { //加密 String encode(CharSequence rawPassword); //校验, 检查一个明文密码是否和一个密文密码一致 boolean matches(CharSequence rawPassword, String encodedPassword); }
- 创建PasswordEncoder实现类
@Component public class MyPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence rawPassword) { return privateEncode(rawPassword); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { // 1.对明文密码加密 String encodedFormPassword = privateEncode(rawPassword); // 比较 return Objects.equals(encodedPassword, encodedFormPassword); } private String privateEncode(CharSequence rawPassword) { try { // 1.创建MessageDigest对象 String algorithm = "MD5"; MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 2.获取rawPassword的字节数组 byte[] input = ((String) rawPassword).getBytes(); // 3.加密 byte[] output = messageDigest.digest(input); // 4.转换为16进制数对应的字符 String encoded = new BigInteger(1, output).toString(16); return encoded; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } }
- 在SpringSecurity的配置类中装配
@Autowired private MyPasswordEncoder myPasswordEncoder; @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception { builder. userDetailsService(userDetailsService).passwordEncoder(myPasswordEncoder); }
- 潜在的风险
- 固定的明文对应固定的密文, 虽然不能从密文通过算法破解反推回明文, 但可以借助彩虹表"碰撞"出来.
- 为解决该问题, 我们可以采用带盐值的加密.
带盐值的加密
- 概念
- 借用生活中烹饪时加食盐的不同, 菜的味道不同. 在加密时每次使用随机生成的盐值, 让加密结果不固定.
- SpringSecurity中使用BCryptPasswordEncoder类完成带盐值的加密.
- BCryptPasswordEncoder
- 加密(encode)
- 使用(SHA-256+随机盐+密钥)把用户输入的密码进行hash处理,得到密码的hash值, 然后将其存入数据库中.
- 加密(encode)
- 测试
public class SecurityTest { public static void main(String[] args) { // 1.创建BCryptPasswordEncoder对象 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); // 2.准备明文字符串 String rawPassword = "123123"; // 3.加密 String encode = passwordEncoder.encode(rawPassword); System.out.println(encode); // $2a$10$L5ENCHehwTCFlTIjvn5sfel6YOG/TWK.SVH/NbTvqI15MaFSQV6D. // $2a$10$J04bVP3gxJghPSrH/t5PJuKw.7KYJvOydQOfI6HTndE/gefetr.SC // $2a$10$QT94LTwIqBw8F3ITsQtxuuiFsOKgBfcdR858xGzYfkh6dGs5FIiCS } } class EncodeTest { public static void main(String[] args) { //1.准备明文字符串 String rawPassword = "123123"; //2.准备密文字符串 String encodedPassword = "$2a$10$QT94LTwIqBw8F3ITsQtxuuiFsOKgBfcdR858xGzYfkh6dGs5FIiCS"; //3.创建BCryptPasswordEncoder对象 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); //4.比较 boolean result = passwordEncoder.matches(rawPassword, encodedPassword); System.out.println(result); } }
- 使用
- 创建BCryptPasswordEncoder对象, 传给passwordEncoder()方法
//每次调用该方法时会检查IOC容器中是否有了对应的bean, 如果有, 就不会真正执行该函数了, 因为bean默认是单例的. //@Scope(value="") - 使用该注解控制是否单例 @Bean public BCryptPasswordEncoder getBCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception { builder .userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder()); ; }
- 创建BCryptPasswordEncoder对象, 传给passwordEncoder()方法