概要
在涉及到密码存储问题上,应该加密的密码,而不是存储明文密码。比如很多大型账号泄露对用户可能造成很大损失,因此应加密 生成不可逆的摘要方式存储。
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如 MD5、SHA 等。一般进行散列时最好提供一个 salt(盐),比如加密密码 “admin”,产生的散列值是 “21232f297a57a5a743894a0e4a801fc3”,可以到一些 md5 解密网站很容易的通过散列值得到密码 “admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是 “密码 + 用户名 +ID”,这样生成的散列值相对来说更难破解。
Shiro 提供了用于加密密码和验证密码服务的 CredentialsMatcher 接口,而 HashedCredentialsMatcher 正是 CredentialsMatcher 的一个实现类。使用更安全的sha256哈希算法或者MD5算法,指定了数据库中密码字段使用base64方式加密。
注意: 创建用户或修改密码时要生成加密密码 SimpleHash
基本步骤
- 在doGetAuthenticationInfo方法返回值创建SimpleAuthenticationInfo对象的时候,需要使用
SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)构造器。 - 使用ByteSource.Util.bytes()来计算盐值
- 盐值需要唯一,一般使用随机字符串或者userid
- 使用new SimpleHash(hashAlgorithmName,crdentials,salt,hashIterations)来计算盐值加密
后的密码的值。
示例代码
配置类中
- 注册加密类HashedCredentialsMatcher
- 在Realm中设置
@Configuration
public class ShiroConfig {
// 加密次数
public static final int HASH_ITERATIONS = 1024;
/**
* 1. 注册自定义Realm
*/
@Bean
public UserRealm realm(HashedCredentialsMatcher hashedCredentialsMatcher) {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return new UserRealm();
}
// ... 其它代码省略
/**
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// 散列算法 MD5
// hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
// 散列算法,sha256算法
hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(HASH_ITERATIONS);
// setStoredCredentialsHexEncoded表示是否存储散列后的密码为16进制,需要和生成密码时的一样
// hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
}
自定义Realm
修改
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
User user = userService.findUserByName(username);
if (user == null) {
throw new UnknownAccountException("账号不存在");
}
if (user.getStatus() == 1) {
throw new LockedAccountException("账号被锁定!请与管理员联系");
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user,
user.getPassword(),
// 设置加密盐 注意要与注册的时保持一致
ByteSource.Util.bytes(user.getUid() + user.getUsername()),
getName()
);
return authenticationInfo;
}
网友评论