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

目录
  • 一:问题引入
  • 二:解决方案
    • 2.1:第一次加密
    • 2.2:第二次加密
  • 三:代码实现
    • 3.1:第一次加密
    • 3.2:第二次加密

一:问题引入

今天看到一篇文章说使用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加密 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java实现md5加密示例

    复制代码 代码如下: /**  * 实现MD5加密  *  */ public class MD5 {    /**   * 获取加密后的字符串   * @param input   * @return   */  public static String stringMD5(String pw) {   try {               // 拿到一个MD5转换器(如果想要SHA1参数换成"SHA1")          MessageDigest messageDigest

  • Java实现MD5加密及解密的代码实例分享

    基础:MessageDigest类的使用 其实要在Java中完成MD5加密,MessageDigest类大部分都帮你实现好了,几行代码足矣: /** * 对字符串md5加密 * * @param str * @return */ import java.security.MessageDigest; public static String getMD5(String str) { try { // 生成一个MD5加密计算摘要 MessageDigest md = MessageDigest.g

  • java中如何使用MD5进行加密

    在各种应用系统的开发中,经常需要存储用户信息,很多地方都要存储用户密码,而将用户密码直接存储在服务器上显然是不安全的,本文简要介绍工作中常用的 MD5加密算法,希望能抛砖引玉. (一)消息摘要简介 一个消息摘要就是一个数据块的数字指纹.即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组).消息摘要是一种与消息认证码结合使用以确保消息完整性的技术.主要使用单向散列函数算法,可用于检验消息的完整性,和通过散列密码直接以文本形式保存等,目前广泛使用的算法

  • java代码实现MD5加密及验证过程详解

    MD5加密 在我们的程序中,不管是什么,都会有安全问题,今天就说的是MD5加密的方法 MD5是哈希算法,也就是 从明文A到密文B很容易,但是从密文B到明文A几乎不可能 也就是说,给你密文,是几乎无法通过解密来得到明文的. 这个一般用于存储密码.也就是数据库里存的是密文,管理员只能看到密文,而看不到明文. 在pom文件导入相关依赖 <!--MD5依赖--> <dependency> <groupId>org.apache.commons</groupId> &

  • Java生成MD5加密字符串代码实例

    (1)一般使用的数据库中都会保存用户名和密码,其中密码不使用明码保存.     有时候用MD5密码,很多语言都提供了将字符串生成为MD5密码的方法或函数.MD5的加密算法是公开的.     有时候也可以用自己的字符串加密算法,这种加密算法是只有自己知道的. (2)破解MD5的过程就是先算好大量或者所有可能的字符串的MD5数值,之后进行查询就可以破解.虽然有些网站规定了密码的位数在6~20位之间,但是要事先计算这么多是字符串并有效的组织存储.查询还是相当麻烦,相当慢的. 因为MD5的位数是固定的,

  • java易懂易用的MD5加密(可直接运行) (1)第1/2页

    1.md5加密,该加密算法是单向加密,即加密的数据不能再通过解密还原.相关类包含在java.security.MessageDigest包中. 2.3-DES加密,该加密算法是可逆的,解密方可以通过与加密方约定的密钥匙进行解密.相关类包含在javax.crypto.*包中. 3.base64编码,是用于传输8bit字节代码最常用的编码方式.相关类在sun.misc.BASE64Decoder 和sun.misc.BASE64Encoder 中. 4.URLEncoder编码,是一种字符编码,保证

  • java 易懂易用的MD5加密(可直接运行)(2)

    程序全文如下:  复制代码 代码如下: package com.neusoft.test.util.crypt; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.text.SimpleDateF

  • JAVA中使用MD5加密实现密码加密

    1.新建Md5.java package com.loger.md5; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import sun.misc.BASE64Encoder; public class Md5 { /**利用MD5进行加密*/ public String Encoder

  • JAVA简单实现MD5注册登录加密实例代码

    开发环境:jdk1.7,eclipse 框架:springmvc,mybatis 工具:maven 以下代码复制即可实现MD5加密 创建一个mave项目,加web.不懂得可以搜索一下就有了. 注册用户的JSP页面代码如下. <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PU

  • java实现MD5加密算法的实例代码

    复制代码 代码如下: package other; import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;/* * MD5 算法*/public class MD5 { // 全局数组    private final static String[] strDigits = { "0", "1", "2", "3", &

随机推荐