【Shiro】shiro加密
⽤户的密码是不允许明⽂存储的,因为⼀旦数据泄露,⽤户的隐私信息会完全暴露。所以密码必须结果加密,⽣成密⽂,然后数据库中只存储⽤户的密码的密⽂。
在加密过程中需要使⽤到⼀些"不可逆加密",如 md5,sha等
所谓不可逆是指:
加密函数A, 明⽂ “abc”,
A("abc") = "密⽂"
不能通过 “密⽂” 反推出 “abc”,即使密⽂泄露密码仍然安全。
1. shiro加密介绍
shiro⽀持hash(散列)加密,常见的如 md5, sha等
-
基本加密过程
md5(明⽂),sha(明⽂) 得到明⽂的密⽂,但明⽂可能⽐较简单,导致密⽂容易被破解。 -
加盐加密过程
系统⽣成⼀个随机salt=“xxxxxx”, md5(明⽂+salt) ,sha(明⽂+salt),则提升了密⽂的复杂度。 -
加盐多次迭代加密过程
如果迭代次数为2,则加密2次: md5(明⽂+salt)=密⽂a , md5(密⽂a+salt)=最终密⽂
sha(明⽂+salt)=密⽂a , sha(密⽂a+salt)=最终密⽂
则进⼀步提升了密⽂的复杂度,和被破解的难度。
加密过程中建议使⽤salt,并指定迭代次数,迭代次数的建议值1000+
- 实例代码:
String password="abc";//密码明⽂
String salt=UUID.randomUUID().toString();//盐
Integer iter = 1000;//迭代次数
String pwd = new Md5Hash(password, salt,iter).toString(); //md5加密
String pwd = new Md5Hash(password, salt, iter).toBase64(); //加密后转base64
String pwd = new Sha256Hash(password, salt, iter).toString();//sha256加密
String pwd = new Sha256Hash(password, salt, iter).toBase64();//加密后转base64
String pwd = new Sha512Hash(password, salt, iter).toString();//sha256加密
String pwd = new Sha512Hash(password, salt, iter).toBase64()//加密后转base64
2. 加密
增加⽤户,或修改⽤户密码时,涉及到密码的加密
在注册⽤户的业务中,对⽤户提交的密码加密即可。
注意:我们需要在用户表中加⼀列【 salt varchar(50) 】,⽤于存储每个⽤户的盐(即随机字符串)。
class UserServiceImpl implements UserService{
@Autowired
private UserDAO userDAO;
public void createUser(User user){
user.setSalt(UUID.randomUUID().toString());//设置随机盐
//设置加密属性:sha256算法,随机盐,迭代1000次
Sha256Hash sha256Hash = new Sha256Hash(user.getPassword(),user.getSalt(),1000);
//将⽤户信息 (包括密码的密⽂ 和 盐) 存⼊数据库
user.setPassword(sha256Hash.toBase64());//密⽂采⽤base64格式化
userDAO.createUser(user);
}
}
3. 密码比对
在登录认证身份的时候,我们需要将登录的密码与正确的密码进行比对,所以我们只需要将登录用的密码加入相同的盐,并进行相同次数的迭代,最后进行比对就行了。
在shiro中,我们只需要声明加密方式,加入的盐,以及次数即可。
首先指定比对器
[main]
...
#声明密码⽐对器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=sha-256
credentialsMatcher.hashIterations=1000
# true=hex格式 false=base64格式
credentialsMatcher.storedCredentialsHexEncoded=false
#⽐对器关联给realm,则realm中对⽤户做身份认证时,可以使⽤加密⽐对器,对密⽂做⽐对
realm1 = com.siyi.realm.MyRealm
realm1.credentialsMatcher=$credentialsMatcher
#realm关联给securityManager
securityManager.realms=$realm1
4. 更改自定义Realm
通过上面的配置我们很容易可以看出还缺少盐的加入。而我们的salt本身就是随机的所以不可能写在配置文件中。所以我们将在程序中从数据库中获取盐,并加入到认证方法中。
doGetAuthenticationInfo⽅法的返回值需要做修改。
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws
AuthenticationException {
String username = (String) token.getPrincipal();
UserService userService =
(UserService)ContextLoader.getCurrentWebApplicationContext().getBean("userService");
User user = userService.queryUser(username);
System.out.println("user:"+user);
if(user==null){
System.out.println("⽤户不存在");
throw new UnknownAccountException("username:"+username+"不存在");
}
//以上逻辑不变
//在最后返回⽤户认证info时,添加⼀个属性:ByteSource.Util.bytes(user.getSalt()) = 盐
//⽤于密码⽐对
return new SimpleAuthenticationInfo(user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),
getName());
}
这样,可以进⾏注册,注册中已经会加密密码。
然后登陆认证身份,认证时realm会调⽤⽐对器⽐对密⽂。