【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會調⽤⽐對器⽐對密⽂。