使用Hash加盐对Password加密
数据表
mis_user
数据表是MIS系统的用户表,所有能登陆MIS系统的帐户都保存在这张表里面。首先你要明确:不是医院所有的工作人员都能登陆MIS系统。例如医护人员可以登录MIS系统,但是保安就不需要用MIS系统,所以我们在mis_user
表中给需要使用MIS系统的用户创建帐户。
序号 | 列名 | 类型 | 备注 |
---|---|---|---|
1 | id | INTEGER | 主键 |
2 | username | VARCHAR | 用户名 |
3 | password | VARCHAR | 密码 |
4 | name | VARCHAR | 姓名 |
5 | sex | VARCHAR | 性别 |
6 | tel | VARCHAR | 电话(用于接收重置密码的短信验证码) |
7 | VARCHAR | 邮箱(用于接收重置密码的邮件验证码) | |
8 | dept_id | INTEGER | 隶属的部门ID |
9 | job | VARCHAR | 职务(医生、护士等) |
10 | ref_id | INTEGER | 关联ID(例如医生ID,护士ID等) |
11 | status | TINYINT | 1有效,2离职,3禁用 |
12 | create_time | DATE | 创建日期 |
使用哈希算法对密码加密
1. 哈希加密
用户的密码需要加密之后再保存到数据库中,这里我选择不可逆的哈希算法加密用户的密码。哈希算法有两种常见的实现方案:MD5和SHA。哪种安全性都挺高的,无法暴力破解。
2. 哈希字典反破解
虽然算法层面破解不了哈希算法,于是就有人另辟蹊径了。他把各种字符串文字都用哈希算法生成加密结果,俗称哈希字典。然后把要破解的哈希值,代入哈希字典,看看能跟哪个记录对得上,于是就得出原始数据是什么了。现在网上就有哈希字典可以下载,所以用哈希加密的结果很容被破解。
3. 多次哈希加密
为了防御哈希字典破解,我们可以对数据做多次哈希加密。比如说第一次用MD5加密,然后再用SHA加密。虽然这么做也能起到一定的防破解作用。
因为哈希值通常是16、32、64个字符组成,所以用哈希字典破解了SHA算法原始数据之后,黑客马上就能识别出来原始数据是用哈希加密过的,于是套用到哈希字典再解密一次。
4. 原始数据加盐混淆
如果我们混淆了原始数据,那么即便黑客破解了原始数据也无法使用。比如说我们对用户原始密码生成哈希值,用哈希值前六位和后三位字符,与原始密码拼接,然后再用哈希算法生成加密结果。即便黑客破解了原始数据,但是这个原始数据并不是用户的密码,黑客用这个混淆过的密码字符串是无法登陆系统的。
String md5 = MD5("原始密码")
String temp = md前六位 + "原始密码" + md后3位
temp = MD5(temp)
当然了还有更狠的混淆做法。例如把先原始密码字符顺序颠倒,然后在每个字符之间插入哈希值的某两个字符。黑客面对这样的混淆结果非常挠头,根本猜不到混淆的字符串哪里是原始密码。
DAO层
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.hospital.api.db.dao.MisUserDao">
<select id="searchUserPermissions" parameterType="int" resultType="String">
SELECT p."permission_code" AS "permission"
FROM HOSPITAL.MIS_USER u
JOIN HOSPITAL.MIS_USER_ROLE ur ON u."id" = ur."user_id"
JOIN HOSPITAL.MIS_ROLE_PERMISSION rp ON rp."role_id" = ur."role_id"
JOIN HOSPITAL.MIS_PERMISSION p ON rp."permission_id" = p."id"
WHERE u."id" = ${userId}
</select>
<select id="login" parameterType="Map" resultType="Integer">
SELECT "id"
FROM HOSPITAL.MIS_USER
WHERE "username" = #{username}
AND "password" = #{password}
</select>
</mapper>
Service层
如果我们混淆了原始数据,那么即便黑客破解了原始数据也无法使用。比如说我们对用户原始密码生成哈希值,用哈希值前六位和后三位字符,与原始密码拼接,然后再用哈希算法生成加密结果。即便黑客破解了原始数据,但是这个原始数据并不是用户的密码,黑客用这个混淆过的密码字符串是无法登陆系统的
package com.example.hospital.api.service.impl;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.example.hospital.api.db.dao.MisUserDao;
import com.example.hospital.api.service.MisUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
@Service
public class MisUserServiceImpl implements MisUserService {
@Resource
private MisUserDao misUserDao;
@Override
public Integer login(Map param) {
String username = MapUtil.getStr(param, "username");
String password = MapUtil.getStr(param, "password");
MD5 md5 = MD5.create();
//用户名的MD5值
String tempName = md5.digestHex(username);
//提取用户名的MD5值前六位
String tempStart = StrUtil.subWithLength(tempName, 0, 6);
//提取用户名的MD5值后三位
String tempEnd = StrUtil.subSuf(tempName, tempName.length() - 3);
password = md5.digestHex(tempStart + password + tempEnd);
//替换password为最新MD5值
param.replace("password", password);
Integer userId = misUserDao.login(param);
return userId;
}
}
当然了还有更狠的混淆做法。例如把先原始密码字符顺序颠倒,然后在每个字符之间插入哈希值的某两个字符。黑客面对这样的混淆结果非常挠头,根本猜不到混淆的字符串哪里是原始密码
Controller层
LoginForm
package com.example.hospital.api.controller.form;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@Data
public class LoginForm {
@NotBlank(message = "username不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{5,50}$", message = "username内容不正确")
private String username;
@NotBlank(message = "password不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{5,50}$", message = "password内容不正确")
private String password;
}
MisUserController
package com.example.hospital.api.controller.form;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.example.hospital.api.common.R;
import com.example.hospital.api.service.MisUserService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/mis_user")
public class MisUserController {
@Resource
private MisUserService misUserService;
@PostMapping("/login")
public R login(@RequestBody @Valid LoginForm form) {
// 把form转换成Map
Map param = BeanUtil.beanToMap(form);
Integer userId = misUserService.login(param);
//登陆成功
if (userId != null) {
//颁发令牌
StpUtil.login(userId);
String token = StpUtil.getTokenValue();
List<String> permissions = StpUtil.getPermissionList();
return R.ok().put("result", true).
put("token", token).
put("permissions", permissions);
}
return R.ok().put("result", false);
}
@GetMapping("/logout")
@SaCheckLogin
public R logout() {
//删除redis缓存token,会从http请求头中提取token,返回空R对象
StpUtil.logout();
return R.ok();
}
}
登录
<script>
import { isUsername, isPassword } from '../utils/validate.js';
import router from '../router/index.js';
export default {
data: function() {
return {
username: null,
password: null,
qrCodeVisible: false,
qrCode: '',
uuid: null,
qrCodeTimer: null,
loginTimer: null
};
},
methods: {
login:function(){
let that=this
//校验表单输入数据的格式正否正确
if(!isUsername(that.username)){
ElMessage({
message: '用户名格式不正确',
type: 'error',
duration: 1200
});
}
else if(!isPassword(that.password)){
ElMessage({
message: '密码格式不正确',
type: 'error',
duration: 1200
});
}
else{
let data = { username: that.username, password: that.password};
that.$http("/mis_user/login","POST",data,true,function(resp){
if(resp.result){
let permissions=resp.permissions
let token=resp.token
//将返回数据写入localStorage
localStorage.setItem("permissions",permissions)
localStorage.setItem("token",token)
router.push({name:"Home"})
}
else{
ElMessage({
message: '登录失败',
type: 'error',
duration: 1200
});
}
})
}
}
}
};
</script>
登出
logout:function(){
let that=this
that.$http('/mis_user/logout',"GET",null,true,function(resp){
localStorage.removeItem("permissions")
localStorage.removeItem("token")
that.$router.push({name:'Login'})
})
}