zoukankan      html  css  js  c++  java
  • BCryptPasswordEncoder加密与MD5加密

    参考文章:https://blog.csdn.net/weixin_42531204/article/details/105254213

    一、使用BcryptPasswordEncoder

    spring security中的BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的。

    1、加密(encode)

    if (user.getPassword() != null) {
                BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
                user.setPassword(encoder.encode(user.getPassword().trim()));
            }

    encode方法的源码

    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);
        }

    注意:每次加密后的密码都不一样,每次的随机盐都保存在加密后的密码中。在比较的时候,随机盐重新被取出。即加密后的密码中,前部分已经包含了盐信息。

    2、密码匹配(matches)

    Result rs = new Result();
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            if (!encoder.matches(user.getPassword(), curUser.getPassword())) {
                rs.setSuccess(false);
                rs.setMsg("旧密码错误!");
                rs.setCode(ResultCode.SUCCESS);
                return rs;
            }

    用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过Hash处理,是不可逆的),而是使用相同的算法把用户输入的密码进行hash处理,得到密码的hash值,然后将其与从数据库中查询到的密码hash值进行比较。如果两者相同,说明用户输入的密码正确。

    这正是为什么处理密码时要用hash算法,而不用加密算法。因为这样处理即使数据库泄漏,黑客也很难破解密码(破解密码只能用彩虹表)。

    3、在spring中使用BcryptPasswordEncoder

    1)、引入依赖

    <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-crypto</artifactId>
                <version>3.1.0.RELEASE</version>
            </dependency>

    2)、使用

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = MySpringBootApplication.class)
    public class EncryptTest {
        @Test
        public void test() {
            String pass = "123456";
            BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
            String hashPass = bcryptPasswordEncoder.encode(pass);
            System.out.println(hashPass);
    
            boolean f = bcryptPasswordEncoder.matches("123456",hashPass);
            System.out.println(f);
    
        }
    }

    结果:

    $2a$10$h8.leCDNRc37ESzTtCXz/usAotyF2oyGOVP5qjcBgzckk4DAf3hfW
    true

    再次执行,结果如下:

    $2a$10$UCNZDxvYpN5RuGWRFaPopOlnoItwimY/o/96ePnIR01UVs2o8bPCa
    true

    可见,每次加密后的结果不一样,但是每次都能匹配成功。

    4、在springboot中使用BcryptPasswordEncoder

    如果只是想使用SpringSecurity + SpringBoot完成密码加密/解密操作,而不使用SpringSecurty提供的其它权证验证功能。具体步骤如下:

     1)、引入依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>

    2)、添加配置类

    我们在添加了spring security依赖后,所有的地址都被spring security所控制了,我们目前只是需要用到BCrypt密码加密的部分,所以我们要添加一个配置类,配置为所有地址都可以匿名访问

    /**
     * 安全配置类
     */
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/**").permitAll()
                    .anyRequest().authenticated()
                    .and().csrf().disable();
        }
    }

    3)、在启动类中配置Bean

    @MapperScan("com.zwh.dao")
    @SpringBootApplication
    public class MySpringBootApplication extends SpringBootServletInitializer {
        public static void main(String[] args) {
            SpringApplication.run(MySpringBootApplication.class);
        }
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(this.getClass());
        }
    
        @Bean
        public BCryptPasswordEncoder bcryptPasswordEncoder(){
            return new BCryptPasswordEncoder();
        }
    }

    4)、使用BCryptPasswordEncoder中的方法完成加密/解密

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = MySpringBootApplication.class)
    public class EncryptTest {
        @Test
        public void test() {
            String pass = "123456";
            BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
            String hashPass = bcryptPasswordEncoder.encode(pass);
            System.out.println(hashPass);
    
            boolean f = bcryptPasswordEncoder.matches("123456",hashPass);
            System.out.println(f);
    
        }
    }

    结果如下:

    $2a$10$S/XfI2AfoDROIZcspSFF7umk/tQsMYVX95PyIzxLrGlrozXuRQ7Ka
    true

    二、MD5加密

    1、第一个工具类

    1)、MD5加密工具类

    public class MD5Tools {
        
        /**
         * 描述:MD5加密方法
         * @param    text:    待加密的文本
         * @return    mdrStr:    MD5加密后的文本
         * @throws NoSuchAlgorithmException 
         * **/
        public static String EncoderByMd5(String text) throws NoSuchAlgorithmException{
            try {
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                BASE64Encoder base64en = new BASE64Encoder();
                return base64en.encode(md5.digest(text.getBytes("UTF-8")));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        
        /**
         * MD5加密
         * @param str
         * @return
         */
        public static String getMD5(String str) {
            MessageDigest messageDigest = null;
    
            try {
                messageDigest = MessageDigest.getInstance("MD5");
    
                messageDigest.reset();
    
                messageDigest.update(str.getBytes("UTF-8"));
            } catch (NoSuchAlgorithmException e) {
                System.out.println("NoSuchAlgorithmException caught!");
                System.exit(-1);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            byte[] byteArray = messageDigest.digest();
    
            StringBuffer md5StrBuff = new StringBuffer();
    
            for (int i = 0; i < byteArray.length; i++) {
                if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
                    md5StrBuff.append("0").append(
                            Integer.toHexString(0xFF & byteArray[i]));
                else
                    md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
            }
    
            return md5StrBuff.toString();
    
        }
        
        public static String getMD5(String str,String charset) {
            MessageDigest messageDigest = null;
    
            try {
                messageDigest = MessageDigest.getInstance("MD5");
    
                messageDigest.reset();
    
                messageDigest.update(str.getBytes(charset));
            } catch (NoSuchAlgorithmException e) {
                System.out.println("NoSuchAlgorithmException caught!");
                System.exit(-1);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            byte[] byteArray = messageDigest.digest();
    
            StringBuffer md5StrBuff = new StringBuffer();
    
            for (int i = 0; i < byteArray.length; i++) {
                if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
                    md5StrBuff.append("0").append(
                            Integer.toHexString(0xFF & byteArray[i]));
                else
                    md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
            }
    
            return md5StrBuff.toString();
    
        }
        
    }

    2)、使用

    // 验证用户名密码
            QueryWrapper<User> wrapper = new QueryWrapper<User>();
            wrapper.eq("user_phone", username).or().eq("user_no", username);
            wrapper.eq("user_pwd", MD5Tools.EncoderByMd5(password));
            User user = userService.getOne(wrapper);

    2、第二个工具类

    public class MD5Utils {
    
        public static Random random = new SecureRandom();
    
        /**
         * 加盐MD5算法
         * @param password
         * @return
         */
        public static String getSaltMD5(String password) throws Exception{
            StringBuilder sb = new StringBuilder(16);
            sb.append(random.nextInt(99999999)).append(random.nextInt(99999999));
            int len = sb.length();
            if(len < 18){
                int diffLen = 16 - len;
                for(int i = 0; i < diffLen; i ++){
                    sb.append(0);
                }
            }
            String salt = sb.toString();
            password = md5Hex(password + salt);
            char[] cs = new char[48];
            for (int i = 0; i < 48; i += 3) {
                cs[i] = password.charAt(i / 3 * 2);
                char c = salt.charAt(i / 3);
                cs[i + 1] = c;
                cs[i + 2] = password.charAt(i / 3 * 2 + 1);
            }
            return String.valueOf(cs);
        }
    
        /**
         * 使用Apache的Hex类实现Hex(16进制字符串和)和字节数组的互转
         * @param str 原明文字符串
         * @return 转换后的字符
         */
        private static String md5Hex(String str) throws Exception{
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(str.getBytes());
            return new String(new Hex().encode(digest));
        }
    
        /**
         * 验证加盐后是否和原文一致
         * @param password 原始密码
         * @param md5str md5后值
         * @return
         */
        public static boolean getSaltverifyMD5(String password, String md5str) throws Exception{
            char[] cs1 = new char[32];
            char[] cs2 = new char[16];
            for (int i = 0; i < 48; i += 3) {
                cs1[i / 3 * 2] = md5str.charAt(i);
                cs1[i / 3 * 2 + 1] = md5str.charAt(i + 2);
                cs2[i / 3] = md5str.charAt(i + 1);
            }
            String salt = new String(cs2);
            return md5Hex(password + salt).equals(String.valueOf(cs1));
        }
    
        public static void main(String[] args) throws Exception {
            System.out.println(getSaltverifyMD5("111111","52da3708888616899226ff6f51c85992b39bd2532639cb79"));
        }
    }

    1)、加密

    jkUser.setPassword(MD5Utils.getSaltMD5(randomPassword));

    2)、验证加盐后是否和原文是否一致

    if (!CollectionUtils.isEmpty(users)) {
                    User user = users.get(0);
                    String password = user.getPassword();
                    boolean saltverifyMD5 = MD5Utils.getSaltverifyMD5(oldPassword, password);
                    if (!saltverifyMD5) {
                        rs.setSuccess(false);
                        rs.setMsg("输入的旧密码错误,请重新输入!");
                        return rs;
                    }
                }
  • 相关阅读:
    MySQL Online DDL导致全局锁表案例分析
    .NET Core教程--给API加一个服务端缓存啦
    任务队列和异步接口的正确打开方式(.NET Core版本)
    .NET Core中使用RabbitMQ正确方式
    .NET Core单元测试之搞死开发的覆盖率统计(coverlet + ReportGenerator )
    没有执行过rm -rf /*的开发不是好运维
    dotnet core在Task中使用依赖注入的Service/EFContext
    可能是全网首个支持阿里云Elasticsearch Xapck鉴权的Skywalking
    dpdk中QSBR具体实现
    C语言二级指针底层实现
  • 原文地址:https://www.cnblogs.com/zwh0910/p/15649091.html
Copyright © 2011-2022 走看看