明文存储
在CRUD中没有任何加秘密操作,数据库中是明文存储的,一旦电脑遭到劫持,密码统统不安全。
MD5加密
MD5 就像给数据做 “数字指纹” 的工具,不管你输入的是一段文字、一张图片还是一个大文件(不管原始数据有多长),它都能输出一个固定长度的 “指纹”—— 通常是 32 位的十六进制字符串。具体怎么做呢?首先会给原始数据补点内容,比如先加个 “1”,再用很多 “0” 凑长度,最后加上原始数据的长度,确保总长度能分成一个个 512 位的小块;接着准备 4 个固定的初始值当 “临时容器”;然后把分好的小块挨个拿来处理,每块都要经过 64 步复杂的位运算(比如移位、对比、合并这些操作),不断更新那 4 个 “容器” 里的值;等所有小块都处理完,把最后 4 个 “容器” 的值拼起来,再转成十六进制字符串,就是这段数据的 MD5 “指纹” 了。它的特点是没法从 “指纹” 反推出原始数据,而且很难找到两个不同数据算出一样的 “指纹”,所以常用在检查文件有没有被改、存简单密码(非特别敏感的场景)这些地方。(AI生成)
使用方法:创建一个MD5工具类:
public class MD5Utils {
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
// 1. 获取MD5摘要算法实例
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有这个md5算法!");
}
// 2. 将字节数组转换为十六进制字符串
String md5code = new BigInteger(1, secretBytes).toString(16);
// 3. 补齐32位长度(不足的前面补0)
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
}
接着在登录,注册,修改密码,忘记密码等用到密码的地方调用该方法即可。如:
/**
* 登录
*/
@IgnoreAuth
@RequestMapping(value = "/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
// 对输入的密码进行MD5加密
String encryptedPassword = MD5Utils.md5(password);
YonghuEntity yonghu = yonghuService.selectOne(new EntityWrapper<YonghuEntity>().eq("username", username));
// 修改密码比较逻辑
if(yonghu==null || !yonghu.getPassword().equals(encryptedPassword))
return R.error("账号或密码不正确");
else if(yonghu.getStatusTypes() == 2)
return R.error("该账号已被冻结");
String token = tokenService.generateToken(yonghu.getId(),username, "yonghu", "用户");
R r = R.ok();
r.put("token", token);
r.put("role","用户");
r.put("username",yonghu.getYonghuName());
r.put("tableName","yonghu");
r.put("userId",yonghu.getId());
return r;
}
/**
* 注册
*/
@IgnoreAuth
@PostMapping(value = "/register")
public R register(@RequestBody YonghuEntity yonghu, HttpServletRequest request) {
Wrapper<YonghuEntity> queryWrapper = new EntityWrapper<YonghuEntity>()
.eq("username", yonghu.getUsername())
.or()
.eq("yonghu_phone", yonghu.getYonghuPhone())
.or()
.eq("yonghu_id_number", yonghu.getYonghuIdNumber())
;
YonghuEntity yonghuEntity = yonghuService.selectOne(queryWrapper);
if(yonghuEntity != null)
return R.error("账户或者联系方式或者身份证号已经被使用");
// 对密码进行MD5加密
yonghu.setPassword(MD5Utils.md5("123456")); // 默认密码加密
yonghu.setNewMoney(0.0);
yonghu.setStatusTypes(1);
yonghu.setCreateTime(new Date());
yonghuService.insert(yonghu);
return R.ok();
}
/**
* 重置密码
*/
@GetMapping(value = "/resetPassword")
public R resetPassword(Integer id, HttpServletRequest request) {
YonghuEntity yonghu = yonghuService.selectById(id);
// 对密码进行MD5加密
yonghu.setPassword(MD5Utils.md5("123456"));
yonghuService.updateById(yonghu);
return R.ok();
}
/**
* 修改密码
*/
@GetMapping(value = "/updatePassword")
public R updatePassword(String oldPassword, String newPassword, HttpServletRequest request) {
YonghuEntity yonghu = yonghuService.selectById((Integer)request.getSession().getAttribute("userId"));
if(newPassword == null){
return R.error("新密码不能为空") ;
}
// 对旧密码和新密码进行MD5加密后比较
if(!yonghu.getPassword().equals(MD5Utils.md5(oldPassword))){
return R.error("原密码输入错误");
}
if(yonghu.getPassword().equals(MD5Utils.md5(newPassword))){
return R.error("新密码不能和原密码一致") ;
}
// 新密码加密存储
yonghu.setPassword(MD5Utils.md5(newPassword));
yonghuService.updateById(yonghu);
return R.ok();
}
MD5加盐加密
"加盐"(Salt)是一种增强安全性的手段,通过在原始数据中加入随机字符串再进行哈希计算,能有效防止彩虹表(预计算哈希值的字典)攻击。
只需要在原有的工具类修改:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
//不建议修改名字,改完还得修改方法里的调用
public class MD5Utils {
// 生成随机盐值
public static String generateSalt() {
// 使用安全的随机数生成器
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16]; // 16字节的盐值
random.nextBytes(salt);
// 转换为Base64字符串方便存储
return Base64.getEncoder().encodeToString(salt);
}
// 带盐值的MD5加密
public static String md5WithSalt(String plainText, String salt) {
try {
// 1. 创建MD5算法实例
MessageDigest md = MessageDigest.getInstance("MD5");
// 2. 将盐值转换为字节数组并更新摘要
md.update(salt.getBytes());
// 3. 将明文转换为字节数组并更新摘要
byte[] digest = md.digest(plainText.getBytes());
// 4. 将字节数组转换为十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
// 转换为两位十六进制数,不足两位前面补0
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5算法不存在", e);
}
}
}