【Spring篇】 项目加盐加密处理

目录

1.  MD5 加密算法

2.  加盐加密流程

3. Spring Security 实现加盐加密

1. 添加 Spring Security 框架

2. 关闭 Spring Security 认证 

3.实现加盐加密


1.  MD5 加密算法

       MD5 是 Message Digest Algorithm 的缩写,译为信息摘要算法,它是 Java 语言中使用很广泛的一种加密算法。MD5 可以将任意字符串,通过不可逆的字符串变换算法,生成一个唯一的 MD5 信息摘要,这个信息摘要也就是我们通常所说的 MD5 字符串。那么问题来了,MD5 安全吗?

答案是 MD5 并不安全 。

原因是 md 5 的每一位 原始密码都对应者着一位固定的密码,也就是说一个字符串的 MD5 永远是不变的,虽然说MD5并不可逆,但是可以被穷举出来,这里就 不得不介绍一下彩虹表了,当我们把每一位数的原始值对应的加密值在同一个列表中列举出来,就构成了一个彩虹表,

当我们知晓了 1 的 MD5 的值为  :c4ca4238a0b923820dcc509a6f75849b

2 的 MD5 的值为 :c81e728d9d4c2f636f067f89cc14862c ,这样我们就可以得到一张彩虹表用来存储

彩虹表
   原始值                                       加密值
       1            c4ca4238a0b923820dcc509a6f75849b
       2            c81e728d9d4c2f636f067f89cc14862c2

彩虹表通常用于恢复由有限集字符组成的固定长度的纯文本密码,也就是说,我们可以使用彩虹表来逐一穷举出原密码的每一位,所以在实际使用过程中数据库如果只是用 md5 进行加密,一把插了钥匙的锁一样不安全。

那么如何解决这种现象来实现加密呢? 

只需要使同一个字符串每一次生成的密码都不一样,实现的关键是使用随机数,也就是 加盐

2.  加盐加密流程

之前使用 MD5 不安全的原因是,每一个原始密码所对应的MD5 的值是固定的,那我们只需要让密码每次加盐之后,生成的最终的密码都不相同,这样就能解决加密不安全的问题了。

  • 使用  MD5 进行加密的流程是: 明文密码  -->     md5(明文密码)   --->  密码
  •  使用加盐加密的流程是 :

1. 构建期 :明文密码 + 随机盐值 

2 . md5(明文密码+随机盐值)

3.将盐值和加密之后的密码存储到数据库(存储到一个字段里边,自己约定一个规则来分离盐值和加密之后的密码)

在实际项目的开发过程中,因为需要将输入的密码和数据库中加盐加密的密码进行验证,所以我们需要在数据库中存入两样东西,一个是加盐加密之后的密码,另一个就是随机盐值

每一个随机盐值就对应了一个彩虹数据库,如果有n个随机盐值,就需要N个不同的彩虹数据库。

那使用这样的约定规则是否有泄漏密码的风险呢?

答案是肯定的,从理论上来讲,目前所有的安全手段都不是绝对安全的,破解上述的加盐密码数据库的破解成本远远高于收益,所以加盐加密的密码相对来说是比较安全的。

  • 模拟实现加盐加密:

实现规则:使用分隔符 $ 来分隔盐值和加密之后的密码

实现思路:将数据库中的 dbpassword 进行解密得到盐值,让用户输入的密码与该盐值进行加盐加密,最后和数据库中存的加盐加密之后的密码进行对比,如果两者相同说明密码匹配,如果两者不同,则说明密码不匹配。

加盐的具体实现步骤:

  1. 使用 UUID 产生一个随机盐值;
  2. 将随机盐值 + 原始密码一起 MD5,产生一个新密码(相同的原始密码,每次都会生成一个不同的新密码);
  3. 将随机盐值 + "$"+上一步生成的新密码加在一起,就是最终生成的密码。

验证加盐加密的具体实现:

  1.  获取数据库中 dbpassword 中的盐值
  2. 用相同的加密方式和步骤,生成一个最终密码和和数据库中保存的加密密码进行对比。
  3. 对比的结果相同,则密码输入正确

/**** 密码工具类* 实现加盐加密 / 加盐解密*/
public class PasswordTools {/*** 加密(加盐处理)* @param password 待加密密码(需要加密的密码)* @return 加密后的密码*/public static String encrypt(String password) {// 随机盐值 UUIDString salt = UUID.randomUUID().toString().replaceAll("-", "");// 密码=md5(随机盐值+密码)String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
//        return salt + "$" + finalPassword;String dbPassword = salt + "$" + finalPassword;return dbPassword;}/*** 解密* @param password       要验证的密码(未加密)* @param  dbPassword 数据库中的加了盐值的密码* @return 对比结果 true OR false*/public static boolean decrypt(String password, String dbPassword) {boolean result = false;if (StringUtils.hasLength(password) && StringUtils.hasLength(dbPassword)) {if (dbPassword.length() == 65 && dbPassword.contains("$")) {String[] dbPasswordArr = dbPassword.split("\\$");// 盐值String salt = dbPasswordArr[0];String finalPassword = dbPasswordArr[1];// 使用同样的加密算法和随机盐值生成最终加密的密码password = DigestUtils.md5DigestAsHex((salt + password).getBytes());if (finalPassword.equals(password)) {result = true;}}}return result;}
}

下面使用一组用例来测试上述代码是否实现了加盐加密和解密验证的过程,此处假定用户的明文密码为 :1234567 

public class Main{public static void main(String[] args){String password = "1234567";String dbPassword = encrypt(password);System.out.println(dbPassword);Scanner s = new Scanner(System.in);while(s.hasNext()){String str = s.nextLine();boolean flg = decrypt(str,dbPassword);System.out.println(flg);}}
}

 以上程序模拟实现了密码加盐加密的过程和进行解密验证的过程,Spring 中内置了 Spring Security 框架,可以很方便的使用相应的方法。

3. Spring Security 实现加盐加密

1. 添加 Spring Security 框架

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

2. 关闭 Spring Security 认证 

如果不关闭 Spring Security 认证的话,就会访问  Spring Security 提供的内置的登录页面,用户名和默认的密码会在控制台打印。

但是此处我们不需要Spring Security 的登录页面,所以需要在启动类中修改,来关闭 Spring Security 的 授权(自动类加载) 

3.实现加盐加密

使用 BCryptPasswordEncoder 类中的 encode() 方法来实现项目的加盐加密,使用 matches ()方法来实现项目的解密验证。

public class Main{ 
public static void main(String[] args) {BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String password = "12345";String dbPassword = passwordEncoder.encode(password);System.out.println(dbPassword);Scanner s = new Scanner(System.in);while(s.hasNext()){String str = s.nextLine();System.out.println(passwordEncoder.matches(str,dbPassword));}}
}

项目登录注册功能实现加盐加密和验证:


@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate PasswordEncoder passwordEncoder;  // 注入密码加密器/*** 实现项目注册功能* @param userInfo* @return*/@RequestMapping("/reg")public AjaxResult reg(UserInfo userInfo) {// 进行非空判断if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword()))return AjaxResult.fail(-1, "参数有误");// 对密码进行加盐加密String salt = generateSalt();  // 生成盐值String encryptedPassword = passwordEncoder.encode(userInfo.getPassword() + salt);  // 加盐加密密码userInfo.setPassword(encryptedPassword);  // 更新用户对象的密码为加密后的密码userInfo.setSalt(salt);  // 设置盐值// 调用 UserService 执行添加方法,并将返回的结果添加 AjaxResult.data 进行返回int result = userService.reg(userInfo);return AjaxResult.success(result);}/*** 实现项目登录功能* @param username* @param password* @param request* @return*/@RequestMapping("/login")public AjaxResult login(String username, String password,HttpServletRequest request) {if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password))return AjaxResult.fail(-1, "参数有误");UserInfo userInfo = userService.login(username);if (userInfo == null || userInfo.getId() <= 0)return AjaxResult.fail(-2, "用户名或密码输入错误!");// 使用密码加密器验证密码是否匹配(包括盐值和加密密码的比较)boolean passwordMatch = passwordEncoder.matches(password + userInfo.getSalt(), userInfo.getPassword());if (!passwordMatch)return AjaxResult.fail(-2, "用户名或密码输入错误!");// 将当前成功登录的用户信息,存储到 session 中去HttpSession session = request.getSession();session.setAttribute(ApplicationVariable.SESSION_KEY_USERINFO, userInfo);return AjaxResult.success(1);}// 生成随机盐值的方法private String generateSalt() {// 根据需要选择适合您的盐值生成方法,这里使用UUID作为示例return UUID.randomUUID().toString().replace("-", "");}
}

当我们注册新用户时,用户的明文密码就会以加盐加密的密码来进行存储,同时也会存储密码加盐时使用的盐值,登录界面时使用存储的盐值来进行用户的登录验证。

 当用户登陆时输入的密码,与存储的盐值进行加盐加密之后的密码 和数据库中存储的用户注册时存储的加盐加密的密码相匹配时,用户登录验证成功。


 总结: 

进行项目的密存储时,不能只是简单的使用 md5 进行加密处理,需要使用随机盐值结合原始密码一起进行 md5 得到加盐加密之后的密码,然后将加盐加密之后的密码和盐值分别存储到数据库当中,这样才真正实现了项目的加盐加密。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/474018.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电梯控制系列之电梯结构介绍

这篇博客介绍单部10层电梯的完整控制程序框架编写过程&#xff0c;编程语言&#xff1a;SCL&#xff0c;控制器型号&#xff1a;S7-1200PLC。本篇博客介绍和电梯控制相关的一些电梯结构介绍。本文只可作为学习参考资料&#xff0c;行业控制需要遵循电梯安全相关规范。 1、电梯…

线程和进程【并发和并行、线程上下文切换、线程的状态】

线程和进程【并发和并行、线程上下文切换、线程的状态】 什么是并发与并行&#xff1f;什么是线程上下文切换&#xff1f;线程状态&#xff1a;一个线程的一生 转自 极客时间 进程&#xff1a;是指内存中运行的一个应用程序&#xff0c;每个进程都有自己独立的内存空间&#x…

JavaSE-03笔记【继承~super】

文章目录 1. 继承1.1 继承概述&#xff08;理解&#xff09;1.2 如何继承&#xff08;掌握&#xff09;1.2.1 继承的语法格式1.2.2 具体举例 1.3 继承的相关特性&#xff08;掌握&#xff09;1.4 对继承自Object类的方法的测试&#xff08;理解&#xff09;1.5 难点解惑1.5.1 掌…

Switch开关(antd-design组件库)简单使用

1.Switch开关 开关选择器。 2.何时使用 需要表示开关状态/两种状态之间的切换时&#xff1b; 和 checkbox 的区别是&#xff0c;切换 switch 会直接触发状态改变&#xff0c;而 checkbox 一般用于状态标记&#xff0c;需要和提交操作配合。 组件代码来自&#xff1a; 开关 Swit…

【力扣】169.多数元素

这道题的解法是运用哈希表打擂台的思想 首先题目的意思是存在数字&#xff0c;意思就是最后返回的结果不可能为空就是了&#xff0c;所以便不用考虑{1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5}这种例子。那么就可以用哈希表存所出现数字出现的次数&#xff0c;然…

11-k8s中网络资源service

一、service资源概述 每当我们企业的业务pod迭代功能的时候&#xff0c;都会修改pod&#xff0c;修改后重新启动pod&#xff0c;ip就会变化&#xff0c;那么在生产环境当中&#xff0c;从用户到宿主机、从宿主机到pod&#xff0c;这一个访问流程&#xff0c;都是事先写好的&…

MySQL数据库基础(一):数据库概述

文章目录 数据库概述 一、数据库介绍 二、数据库分类 1、关系型数据库 2、非关系型数据库NoSQL 三、常见数据库介绍 1、关系型数据库 2、非关系型数据库 数据库概述 一、数据库介绍 数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;按照特定的…

租用一个服务器需要多少钱?2024阿里云新版报价

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

小白必看,总结前端所有主流的构建工具,webpack / vite / roollup / esbuild,包含源码,建议关注+收藏

前言 本篇文章旨在总结前端常见的构建工具&#xff0c;构建工具是前端工程化中的重要的组成部分。 在实际项目中&#xff0c;我们初始化项目&#xff0c;一般是使用脚手架命令一键生成的&#xff0c;比如说使用 create-vue 初始化 vue 项目的时候&#xff0c;就会默认使用 vi…

树莓派:使用mdadm为重要数据做RAID 1保护

树莓派作为个人服务器可玩性还是有点的。说到服务器&#xff0c;在企业的生成环境中为了保护数据&#xff0c;基本上都会用到RAID技术。比如&#xff0c;服务器两块小容量但高性能的盘做个RAID-1按装操作系统&#xff0c;余下的大容量中性能磁盘做个RAID-5或者RAID-6存放数据。…

第13章 网络 Page741~744 asio核心类 ip::tcp::socket

1. ip::tcp::socket liburl库使用"curl*" 代表socket 句柄 asio库使用ip::tcp::socket类代表TCP协议下的socket对象。 将“句柄”换成“对象”,因为asio库是不打折扣的C库 ip::tcp::socket提供一下常用异步操作都以async开头 表13-3 tcp::socket提供的异步操作 …

字节UC伯克利新研究 | Magic-Me:简单有效的主题ID可控视频生成框架

在生成模型领域&#xff0c;针对特定身份&#xff08;ID&#xff09;创建内容已经引起了极大的兴趣。在文本到图像生成&#xff08;T2I&#xff09;领域&#xff0c;以主题驱动的内容生成已经取得了巨大的进展&#xff0c;使图像中的ID可控。然而&#xff0c;将其扩展到视频生成…