您现在的位置是:群英 > 开发技术 > 编程语言
Java双重MD5加密的实现思路和代码是什么
Admin发表于 2022-10-11 09:18:02474 次浏览
在这篇文章中我们将学习“Java双重MD5加密的实现思路和代码是什么”的相关知识,下文有详细的介绍及实例,步骤过程清晰,简单易懂,小编觉得挺不错的,有需要的朋友可以借鉴参考,希望大家阅读完这篇能有所获。

一:问题引入

今天看到一篇文章说使用MD5对密码进行加密存储也还不能做到很安全,网上有在线解密MD5的网站,我一搜,还真有。接下来我尝试对我存储在数据库中的密码进行解密操作:

 可以看到成功将我的密码解密出来,这让我很吃惊,因为我们都知道MD5算法是不可逆的,因为它是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。那么为什么网站中可以将我的密码解密出来呢?

经过一番查找后发现,原来在线解密工具的解密原理很简单,其原理是收集用户常用的简单密码形成了一个密码字典,并将字典中的密码用MD5加密后存储起来,在所谓的“解密“的时候,就将真正用户密码加密都的密文与已存储的密码相比较,如该密文存在于字典当中,即可以“解密”。因此,简单的用MD5对用户密码加密还不安全,我们设置密码时候也通常都会有复杂度检测,比如密码必须带英文数字什么的,就是为了安全性考虑。

知道其解密原理之后,我们尝试一下复杂点的密码看看能不能“解密”出来,首先对密码“qweasd666”进行加密:

 加密之后我们选取32位小写的密文进行解密操作:

 可以看到解密失败,说明其原理就是我们上面所说的收集常用的密文进行一一对应。既然解密失败了,那么说明“qweasd666”这个密码是安全的,你们可以都设置这个密码(doge)。

言归正传,说到这个网站成功将我精心设置的密码给破解了,这让我很没有安全感,而且我感觉我很没有面子,我一定要将我的登录安全等级进行提升。

除了上面提到的解密操作之外,还有一个很大的问题就是在前端将数据传输过来时候http采用的是明文传输,如果传输数据包被截取,那么就算你后端的加密算法有多复杂,你的密码也会被别人知道。

二:解决方案

2.1:第一次加密

找到问题所在之后,我们就可以对症下药了,首先我觉得要解决的是http明文传输问题,因为这个风险最大,普通抓包就能抓取密码,这不是很恐怖的一件事吗。解决方案也很简单,既然明文传输不安全,那么我们加密后再进行传输不就行了吗?

我选用的方案仍然还是MD5加密,但是对密码加密前还要加入一个固定salt。salt?是加盐吗,其实差不多,更不如说是加点“佐料”。其基本想法是这样的:当用户首次提供密码时(通常是注册时),由程序往这个密码里撒一些“佐料”,为了减轻开发压力,这个佐料对于每一个用户都是相同的,然后再散列。这样就能防止传输过程中出现明文密码泄露。

2.2:第二次加密

注意上面我提到的是防止出现明文密码泄漏,但是这并不代表密文不会泄漏,假如黑客截取你通过http传输的经过加密后的密码,或者数据库发生泄漏导致加密后的密码被黑客盗取,黑客通过解析前端js文件获得前端固定盐值,那么黑客可能可以通过彩虹表进行反向查询得到原始密码。这时候就二次加密的重要性就出来了,二次加密实现原理为对前端传过来经过加密之后的密码再次和一个随机Salt值结合后进行加密(注意这次是随机的),盐值会在用户登陆的时候随机生成,并存在数据库中。

这时候可能你会和我刚开始一样也会有一个疑惑,那就是你把随机盐值保存到数据库中了,那么如果数据库泄漏那你的随机盐值还有什么作用呢?黑客拿到加密数据进行解密后去除盐值不是就能得到密码了吗?是的,黑客有可能通过这样的手段得到密码,但是前提是他能解密出来,要知道的是MD5是无法直接破解的,只能通过穷举法进行解密。就算黑客拿到了数据库中的加密密码,但是不知道后端的加密过程,他就无法进行解析,就算黑客同时知道加密过程,由于经过了二次加盐二次加密,这时候的密文是很难很难被解析出来的,随着加密数据的复杂度增加,破解成本是呈指数级增加的,在那么大的成本面前相信没有什么黑客愿意去尝试。当然,salt也不一定要加在最前面或最后面,也可以插在中间,也可以分开插入,也可以倒序,程序设计时可以灵活调整,都可以使破解的难度呈指数型增长。

2.3:具体实现

2.3.1:用户注册

  • 前端对用户输入的密码进行md5加密(固定的salt)
  • 将加密后的密码传递到后端
  • 后端随机生成一个salt
  • 使用生成salt对前端传过来的密码进行加密,然后将加密后密码和salt一起保存到db中

2.3.2:用户登录

  • 前端对用户输入的密码进行md5加密(固定的salt)
  • 将加密后的密码传递到后端
  • 后端使用用户账号取出用户信息
  • 后端对加密后的密码在进行md5加密(取出盐),然后与数据库中存储的密码进行对比
  • 匹配则登录成功,否则登录失败

三:代码实现

3.1:第一次加密

3.1.1:前端

const params = {
    ...this.ruleForm,
    sex: this.ruleForm.sex === '女' ? '0' : '1',
    //设置密码加密(加上固定salt值)
    password: md5(this.ruleForm.password + this.salt)
}
addEmployee(params).then(res => {
    if (res.code === 1) {
        this.$message.success('员工添加成功!')
        if (!st) {
            this.goBack()
        } else {
            this.ruleForm = {
                username: '',
                'name': '',
                'phone': '',
                password: '',
                // 'rePassword': '',/
                'sex': '男',
                'idNumber': ''
            }
        }
    } else {
        this.$message.error(res.msg || '操作失败')
    }
}

3.1.2:后端

/**
* 添加员工
*/
@PostMapping
public R<String> save(@RequestBody Employee employee){
    //生成随机salt值
    String salt = RandomStringUtils.randomAlphanumeric(5);
    //设置随机盐值
    employee.setSalt(salt);
    //设置密码二次加密
    employee.setPassword(DigestUtils.md5DigestAsHex((salt + employee.getPassword()).getBytes()));
 
    boolean save = employeeService.save(employee);
    if(save){
        return R.success("添加成功!");
    }
    return R.error("添加失败!");
}

3.2:第二次加密

3.2.1:前端

const params = {
    ...this.loginForm,
    //登录密码加上固定盐值后发送
    password: md5(this.loginForm.password + this.salt),
}
let res = await loginApi(params)
if (String(res.code) === '1') {
    localStorage.setItem('userInfo', JSON.stringify(res.data))
    window.location.href = '/backend/index.html'
} else {
    this.$message.error(res.msg)
    this.loading = false
}

3.2.2:后端

/**
 * 员工登录
 */
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
    //1.获取传输过来的加密后的密码值
    String encryPassword = employee.getPassword();
 
    //2.从数据库中获取用户信息
    LambdaQueryWrapper<Employee> lqw = new LambdaQueryWrapper<>();
    lqw.eq(Employee::getUsername,employee.getUsername());
    Employee emp = employeeService.getOne(lqw);
 
    //3.如果没有查询到则返回登陆失败查询结果
    if(emp == null){
        return R.error("没有查询到该用户信息!");
    }
 
    //4.获取注册时保存的随机盐值
    String salt = emp.getSalt();
 
    //5.将页面提交的密码password进行md5二次加密
    String password = DigestUtils.md5DigestAsHex((salt + encryPassword).getBytes());
 
    //6.密码比对,如果不一致则返回登陆失败结果
    if(!emp.getPassword().equals(password)){
        return R.error("密码错误!");
    }
 
    //7.查看员工状态是否可用
    if(emp.getStatus() == 0){
        return R.error("该员工已被禁用!");
    }
    
    //8.登录成功,将员工id存入Session对象并返回登录成功结果
    request.getSession().setAttribute("employee",emp.getId());
    return R.success(emp);
}



关于“Java双重MD5加密的实现思路和代码是什么”的内容就介绍到这,感谢各位的阅读,相信大家对Java双重MD5加密的实现思路和代码是什么已经有了进一步的了解。大家如果还想学习更多知识,欢迎关注群英网络,小编将为大家输出更多高质量的实用文章!

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

相关信息推荐
2022-07-30 17:23:52 
摘要:在css中,outline指的是设置元素轮廓的属性,可以指定元素轮廓的样式、颜色和宽度。outline是一个简写属性,可以在一个声明中按顺序同时设置样式、颜色和宽度,语法“outline:outline-color outline-style outline-width;”;如果不设置其中的某个值,也不会出问题。
2022-06-30 17:55:47 
摘要:Golang判断文件是否能写入的方法:在syscall.Access中传入文件和文件权限代码,然后根据返回值即可判断是否可以写入。
2022-04-28 17:17:33 
摘要:对于安卓程序员来说,自定义view简直不要太重要,毕竟有很多功能,譬如圆形头像这些,用单纯的原生非常难以实现,而用自定义view,简直分分钟,今天我们来实现自定义圆角输入框和按钮,大家可以跟着练习,掌握技巧
云活动
推荐内容
热门关键词
热门信息
群英网络助力开启安全的云计算之旅
立即注册,领取新人大礼包
  • 联系我们
  • 24小时售后:4006784567
  • 24小时TEL :0668-2555666
  • 售前咨询TEL:400-678-4567

  • 官方微信

    官方微信
Copyright  ©  QY  Network  Company  Ltd. All  Rights  Reserved. 2003-2019  群英网络  版权所有   茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078   粤ICP备09006778号
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
微信公众号
返回顶部
返回顶部 返回顶部