1.为什么要盐值加密
对于同一密码,同一加密算法会产生相同的hash值。这样,当用户进行身份验证时,对用户输入的明文密码应用相同的hash加密算法,得出一个hash值,然后使用该hash值和之前存储好的密文值进行对照,如果两个值相同,则密码认证成功,否则密码认证失败。出于更安全的考虑,即使两个用户输入的是相同的密码,也应该要保存为不同的密文,即使用户输入的是弱密码,也需要考虑进行增强,从而增加密码被攻破的难度。因此出现了加盐加密。
那么盐值加密是怎么加密的呢,用下面的图来解释:
加密过程(用户注册)
也就是说最终的密文是以下两个内容的函数
- 用户输入的密码明文
- 盐值
最后要将以下内容存入数据库:
- 用户的用户名
- 最终的密文
- 加密所用的盐值
校验过程(用户登录)
2.在Springboot项目中如何利用Shrio进行密码加密
1.在ShiroConfig中为自定义UserRealm对象添加密码匹配凭证管理器HashedCredentialsMatcher
部分代码如下:
//创建Realm 对象,需要用户自定义
@Bean(name = "UserRealm")
public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(matcher);
return userRealm;
}
/**
* 密码匹配凭证管理器
*
* @return
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 采用MD5方式加密
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
2.修改UserRealm认证中AuthenticationInfo
/**
* 认证
*
* @param token
* @return
* @throws AuthenticationException
*/
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
/* System.out.println("执行了=》doGetAuthenticationInfo");*/
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
//连接真实的数据库 用户名:密码 数据库中取
User user = userService.queryUserByname(usertoken.getUsername());
// 获取盐值,即用户名
ByteSource salt = ByteSource.Util.bytes(usertoken.getUsername());
//没有当前用户
//抛出一个异常 LoginController中的login方法
if (user == null) {
return null;
}
//密码验证:Shiro做
//自动认证 第一个参数传递User
// 最后一个参数为当前realm对象的名称,调用分类的getName()
return new SimpleAuthenticationInfo(user, user.getPwd(), salt, getName());
}
同样的是登录Controller和之前写法一样
//获得当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
//执行登录方法,如果没有异常就说明ok了
return "redirect:/main.html";
} catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名错误");
return "index";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "index";
}
不过在注册界面我们就需要对密码进行盐值加密了,这里Shiro为我们提供了加密api
public String registerUser(@RequestBody User user) {
// 将用户名作为盐值
ByteSource salt = ByteSource.Util.bytes(user.getName());
/*
* MD5加密:
* 使用SimpleHash类对原始密码进行加密。
* 第一个参数代表使用MD5方式加密
* 第二个参数为原始密码
* 第三个参数为盐值,即用户名
* 第四个参数为加密次数
* 最后用toHex()方法将加密后的密码转成String
* */
String newPs = new SimpleHash("MD5", user.getPwd(), salt, 1024).toHex();
user.setPwd(newPs);
user.setPerms(user.getPerms());
userService.registerUser(user);
return "register";
}
需要注意的是,如果数据库中的密码不是加密后的会报错。
而我们利用这种加密方式,上一个文章的手撸自动登录就GG了,因为存入session中的是加密过后的密码,不能利用加密后的密码直接登录。