Shiro安全框架學習03 - 編碼/加密

md5加密

在涉及密碼存儲問題上,應該進行加密存儲,而不能是明文,否則賬號密碼泄露,就會產生意想不到的後果。所以,通常都會採用非對稱加密,什麼是非對稱呢,就是不可逆的,而md5就是這樣一個算法。
如123456用md5加密後,得到字符串:e10adc3949ba59abbe56e057f20f883e

String password = "123456";
String encodedPassword = new Md5Hash(password).toString();
System.out.println(encodedPassword);

上面講了md5加密,但md5加密也有一些缺陷:如果我的密碼是123456,你的也是123456,那麼md5的值是一樣的,通過比較加密後的字符串,就可以反推過來。
雖然md5不可逆,但可以用窮舉法,把一些常用的密碼的md5值記錄下來,比如123,abcde等等,這樣也可以破解一部分。
爲了解決這個問題,引入了鹽(salt)的概念,雖然每次123456的md5值都是e10adc3949ba59abbe56e057f20f883e,但是加上鹽之後,即123456+隨機數,那麼md5值就不一樣了,這個隨機數 就是鹽,而這個隨機數也會在數據庫裏保存下來,每個不同的用戶,隨機數也是不一樣的。再者就是加密次數,可以加密一次 也可以加密多次,加密次數越多,密文的安全性也就越高。

String password = "123456";
String salt = new SecureRandomNumberGenerator().nextBytes().toString(); //隨機數:鹽
int times = 2; //加密次數
String algorithmName = "md5"; //散列算法
 
String encodedPassword = new SimpleHash(algorithmName,password,salt,times).toString();
System.out.println("密文:" + encodedPassword);

獲取密文和鹽

public class PasswordHelper {

	public static void main(String[] args) {
		String algorithmName = "md5";
		String username = "itmyhome";
		String password = "123456";
		String salt1 = password;
		String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
		int hashIterations = 2;
		SimpleHash hash = new SimpleHash(algorithmName, password,
				salt1 + salt2, hashIterations);
		String encodedPassword = hash.toHex();
		//每次輸出結果都不一樣
		System.out.println("密文:" + encodedPassword); //b4b88b2ef28089f9c802df11bd216a33
		System.out.println("鹽:" + salt2); //713183f112f48824613f0f05d0d10060

	}

}

修改Realm

public class UserRealm extends AuthorizingRealm  {

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		// TODO Auto-generated method stub
		
		//獲取賬號密碼
		UsernamePasswordToken t = (UsernamePasswordToken) token;
        String password = new String(t.getPassword());
		String username = (String) token.getPrincipal();
		
		//獲取數據庫種的密碼、鹽(此處寫死,測試用)
		User user = new User();
		user.setUsername(username);
		user.setPassword("b4b88b2ef28089f9c802df11bd216a33");
		user.setSalt("713183f112f48824613f0f05d0d10060");
		
		if (!"itmyhome".equals(username)) {
			// 拋出 帳號找不到異常
			throw new UnknownAccountException();
		}
		
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
				user.getUsername(), // 用戶名
				user.getPassword(), // 密碼
				ByteSource.Util.bytes(password + user.getSalt()),// salt=password+salt
				getName() // realm name
		);
		return authenticationInfo;
	}

}

ini配置

[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher  
credentialsMatcher.hashAlgorithmName=md5  
credentialsMatcher.hashIterations=2  
credentialsMatcher.storedCredentialsHexEncoded=true  

userRealm=com.itmyhome.UserRealm
securityManager.realms=$userRealm
#這行很重要
userRealm.credentialsMatcher=$credentialsMatcher

通過credentialsMatcher.hashAlgorithmName=md5指定散列算法爲md5,需要和生成密碼時的一樣
credentialsMatcher.hashIterations=2,散列迭代次數
credentialsMatcher.storedCredentialsHexEncoded=true表示是否存儲散列後的密碼爲16進制
此處最需要注意的就是HashedCredentialsMatcher的算法需要和生成密碼時的算法一樣。
另外HashedCredentialsMatcher會自動根據AuthenticationInfo的類型是否是SaltedAuthenticationInfo來獲取credentialsSalt鹽。

測試類

public class ShiroTest {

	private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

	public static void main(String[] args) {
      
		//獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager  
        Factory<SecurityManager> factory =  new IniSecurityManagerFactory("classpath:auth.ini");
 
        //得到SecurityManager實例 並綁定給SecurityUtils  
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        
        //得到Subject及創建用戶名/密碼身份驗證Token(即用戶身份/憑證)  
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("itmyhome","123456");
        
        try{
            //登錄,即身份驗證  
        	subject.login(token);
        }catch(org.apache.shiro.authc.AuthenticationException e){
        	//身份驗證失敗
        	e.printStackTrace();
        }
        
        System.out.println(subject.getPrincipal());
        System.out.println("User is authenticated:  " + subject.isAuthenticated());
    }
}
發佈了691 篇原創文章 · 獲贊 2948 · 訪問量 409萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章