qidao123.com技术社区-IT企服评测·应用市场

标题: SpringSecurity5(3-密码剖析器) [打印本页]

作者: 曹旭辉    时间: 2025-3-12 22:07
标题: SpringSecurity5(3-密码剖析器)
PasswordEncoder
  1. public interface PasswordEncoder {
  2.     //加密
  3.     String encode(CharSequence var1);
  4.     //比较密码
  5.     boolean matches(CharSequence var1, String var2);
  6.        
  7.     default boolean upgradeEncoding(String encodedPassword) {
  8.         return false;
  9.     }
  10. }
复制代码

工作流程

WebSecurityConfigurerAdapter 初始化密码剖析器时,如果没有自定义 Bean 的话,会默认初始化 DelegatingPasswordEncoder



DaoAuthenticationProvider 在 additionalAuthenticationChecks 方法中会获取 Spring 容器中的 PasswordEncoder 来对用户输入的密码进行比力
BCryptPasswordEncoder 密码匹配流程
  1. public class BCryptPasswordEncoder implements PasswordEncoder {
  2.     private Pattern BCRYPT_PATTERN = Pattern
  3.             .compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
  4.     private final Log logger = LogFactory.getLog(getClass());
  5.     private final int strength;
  6.     private final BCryptVersion version;
  7.     private final SecureRandom random;
  8.    
  9.     /**
  10.      * 参数:原密码
  11.      */
  12.     public String encode(CharSequence rawPassword) {
  13.         if (rawPassword == null) {
  14.             throw new IllegalArgumentException("rawPassword cannot be null");
  15.         }
  16.         String salt;
  17.         if (random != null) {
  18.             salt = BCrypt.gensalt(version.getVersion(), strength, random);
  19.         } else {
  20.             salt = BCrypt.gensalt(version.getVersion(), strength);
  21.         }
  22.         return BCrypt.hashpw(rawPassword.toString(), salt);
  23.     }
  24.     /**
  25.      * 参数一:原密码,
  26.      * 参数二:加密后保存在数据库的密码
  27.      */
  28.     public boolean matches(CharSequence rawPassword, String encodedPassword) {
  29.         if (encodedPassword == null || encodedPassword.length() == 0) {
  30.             logger.warn("Empty encoded password");
  31.             return false;
  32.         }
  33.         if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
  34.             logger.warn("Encoded password does not look like BCrypt");
  35.             return false;
  36.         }
  37.         return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
  38.     }
  39. }
复制代码
从数据库得到的“密码”(参数: salt )进行一系列校验(长度校验等)并截取“密码”中相应的密码盐,利用这个密码盐进行同样的一系列计算 Hash 操作和 Base64 编码拼接一些标识符生成所谓的“密码”,最后 equalsNoEarlyReturn 方法对同一个密码盐生成的两个“密码”进行匹配
  1. public class BCrypt {
  2.    
  3.     public static String hashpw(String password, String salt) {
  4.         byte passwordb[];
  5.         passwordb = password.getBytes(StandardCharsets.UTF_8);
  6.         return hashpw(passwordb, salt);
  7.     }
  8.    
  9.     public static String hashpw(byte passwordb[], String salt) {
  10.         BCrypt B;
  11.         String real_salt;
  12.         byte saltb[], hashed[];
  13.         char minor = (char) 0;
  14.         int rounds, off;
  15.         StringBuilder rs = new StringBuilder();
  16.         if (salt == null) {
  17.             throw new IllegalArgumentException("salt cannot be null");
  18.         }
  19.         int saltLength = salt.length();
  20.         if (saltLength < 28) {
  21.             throw new IllegalArgumentException("Invalid salt");
  22.         }
  23.         if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
  24.             throw new IllegalArgumentException ("Invalid salt version");
  25.         if (salt.charAt(2) == '$')
  26.             off = 3;
  27.         else {
  28.             minor = salt.charAt(2);
  29.             if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b')
  30.                     || salt.charAt(3) != '$')
  31.                 throw new IllegalArgumentException ("Invalid salt revision");
  32.             off = 4;
  33.         }
  34.         // Extract number of rounds
  35.         if (salt.charAt(off + 2) > '$')
  36.             throw new IllegalArgumentException ("Missing salt rounds");
  37.         if (off == 4 && saltLength < 29) {
  38.             throw new IllegalArgumentException("Invalid salt");
  39.         }
  40.         rounds = Integer.parseInt(salt.substring(off, off + 2));
  41.         real_salt = salt.substring(off + 3, off + 25);
  42.         saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
  43.         if (minor >= 'a') // add null terminator
  44.             passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
  45.         B = new BCrypt();
  46.         hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
  47.         rs.append("$2");
  48.         if (minor >= 'a')
  49.             rs.append(minor);
  50.         rs.append("$");
  51.         if (rounds < 10)
  52.             rs.append("0");
  53.         rs.append(rounds);
  54.         rs.append("$");
  55.         encode_base64(saltb, saltb.length, rs);
  56.         encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
  57.         return rs.toString();
  58.     }
  59.     public static boolean checkpw(String plaintext, String hashed) {
  60.         return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
  61.     }
  62.     public static boolean equalsNoEarlyReturn(String a, String b) {
  63.         return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8), b.getBytes(StandardCharsets.UTF_8));
  64.     }
  65.    
  66.     // ......
  67. }
复制代码
自定义密码剖析器
  1. public class MyMD5PasswordEncoder implements PasswordEncoder {
  2.     /**
  3.      * 加密
  4.      * @param charSequence  明文字符串
  5.      */
  6.     @Override
  7.     public String encode(CharSequence charSequence) {
  8.         try {
  9.             MessageDigest digest = MessageDigest.getInstance("MD5");
  10.             return toHexString(digest.digest(charSequence.toString().getBytes()));
  11.         } catch (NoSuchAlgorithmException e) {
  12.             e.printStackTrace();
  13.             return "";
  14.         }
  15.     }
  16.     /**
  17.      * 密码校验
  18.      * @param charSequence 明文,页面收集密码
  19.      * @param s 密文 ,数据库中存放密码
  20.      */
  21.     @Override
  22.     public boolean matches(CharSequence charSequence, String s) {
  23.         return s.equals(encode(charSequence));
  24.     }
  25.     /**
  26.      * @param tmp 转 16 进制字节数组
  27.      * @return 饭回 16 进制字符串
  28.      */
  29.     private String toHexString(byte [] tmp){
  30.         StringBuilder builder = new StringBuilder();
  31.         for (byte b :tmp){
  32.             String s = Integer.toHexString(b & 0xFF);
  33.             if (s.length()==1){
  34.                 builder.append("0");
  35.             }
  36.             builder.append(s);
  37.         }
  38.         return builder.toString();
  39.     }
  40. }
复制代码
  1. @Configuration
  2. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  3.     @Bean
  4.     public PasswordEncoder passwordEncoder(){
  5.         //return new BCryptPasswordEncoder(); //自带的
  6.         return new MyMD5PasswordEncoder();
  7.     }
  8. }
复制代码
  1. @Slf4j
  2. @Component
  3. public class UserDetailServiceImpl implements UserDetailsService {
  4.     @Autowired
  5.     private PasswordEncoder passwordEncoder;
  6.     @Override
  7.     public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  8.         String password = passwordEncoder.encode("123");
  9.         log.info("登录用户:{},密码:{}", s, password);
  10.         return new User(s, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
  11.     }
  12. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4