在有“密码加盐”方法之前,一般做法是直接对密码进行散列,理论上散列值不可逆,但黑客或者内部人士在获取密码散列值后,再通过查散列值字典(例如MD5密码破解网站),就可能知道对应的密码明文。
通过加盐处理,可以实现相同的密码明文,产生不同的密码值,减少被破解的风险。
shiro支持密码加密,或不加密。与ShiroConfig.hashedCredentialsMatcher方法密切相关。
在上篇【Spring Boot中引入Jpa和Shiro】的基础上进行密码加盐。
JpaController.java 在add方法中,增加密码加盐逻辑
@GetMapping(path = "/jpa/add")
public String addNewUser(@RequestParam String name,
@RequestParam String username, @RequestParam String password) {
JpaUser user = new JpaUser();
user.setName(name);
user.setUsername(username);
// uuid获取随机字符串,作为盐值。
String salt = StringUtil.getUUID();
user.setSalt(salt);
//add Salt to password
//user.getCredentialsSalt我没有直接使用盐,而是将用户名和盐作为密码加密的最终盐值
SimpleHash hash = new SimpleHash(hcm.getHashAlgorithmName(),
password, user.getCredentialsSalt(), hcm.getHashIterations());
//重新赋值
user.setPassword(hash.toString());
userRepository.save(user);
//重定向到 根目录index
return "redirect:/index";
}
MyShiroRealm.java 生成SimpleAuthenticationInfo实例时,使用加盐处理
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
String username = (String) token.getPrincipal();
JpaUser userInfo = null;
List<JpaUser> userInfoList = userRepository.findByUsername(username);
if (userInfoList == null) {
return null;
} else if (userInfoList.size() <= 0) {
return null;
} else {
userInfo = userInfoList.get(0);
}
SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(
userInfo,
userInfo.getPassword(),
//salt=username+salt
ByteSource.Util.bytes(userInfo.getCredentialsSalt()),
getName() // realm name
);
return authInfo;
}
StringUtil.java 获取随机字符串工具类
public class StringUtil {
public static String getUUID() {
String uuid = UUID.randomUUID().toString();
// 去掉字符串中的"-"符号
uuid = uuid.replaceAll("-", "");
return uuid;
}
/**
* 生成指定长度的随机字符串(0-9a-zA-Z)
*/
public static String getRandomString(int length) {
// 产生随机数
Random random = new Random();
StringBuffer sb = new StringBuffer();
// 循环length次
for (int i = 0; i < length; i++) {
// 产生0-2个随机数,既与a-z,A-Z,0-9三种可能
int number = random.nextInt(3);
long result = 0;
switch (number) {
// 如果number产生的是数字0;
case 0:
// 产生A-Z的ASCII码
result = Math.round(Math.random() * 25 + 65);
// 将ASCII码转换成字符
sb.append(String.valueOf((char) result));
break;
case 1:
// 产生a-z的ASCII码
result = Math.round(Math.random() * 25 + 97);
sb.append(String.valueOf((char) result));
break;
case 2:
// 产生0-9的数字
sb.append(String.valueOf(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
}