目录
一:问题引入
二:解决方案
3.1:第一次加密
3.2:第二次加密
三:代码实现
首页 Java java教程 Java双重MD5加密怎么实现安全登录

Java双重MD5加密怎么实现安全登录

May 17, 2023 pm 05:31 PM
java md5

一:问题引入

对存储在数据库中的密码进行解密操作:

Java双重MD5加密怎么实现安全登录

Java双重MD5加密怎么实现安全登录

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

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

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

Java双重MD5加密怎么实现安全登录

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

Java双重MD5加密怎么实现安全登录

解密失败表明其原理是通过收集常用的密文进行匹配破解。既然“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) === &#39;1&#39;) {
    localStorage.setItem(&#39;userInfo&#39;, JSON.stringify(res.data))
    window.location.href = &#39;/backend/index.html&#39;
} 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加密怎么实现安全登录的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java 中的随机数生成器 Java 中的随机数生成器 Aug 30, 2024 pm 04:27 PM

Java 随机数生成器指南。在这里,我们通过示例讨论 Java 中的函数,并通过示例讨论两个不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

Java 中的时间戳至今 Java 中的时间戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的时间戳到日期指南。这里我们还结合示例讨论了介绍以及如何在java中将时间戳转换为日期。

创造未来:面向零基础的 Java 编程 创造未来:面向零基础的 Java 编程 Oct 13, 2024 pm 01:32 PM

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。

See all articles