考慮系統密碼的安全,目前大多數系統都不會把密碼以明文的形式存放到數據庫中。java
一把會採起如下幾種方式對密碼進行處理算法
密碼的存儲數據庫
「編碼」存儲apache
Shiro 提供了 base64和 16 進制字符串編碼/解碼的 API支持,方便一些編碼解碼操做。 Shiro內部的一些數據的存儲/表示都使用了 base64和 16 進制字符串。安全
下面兩端代碼分別對其進行演示app
Stringstr = "hello"; Stringbase64Encoded = Base64.encodeToString(str.getBytes()); Stringstr2 = Base64.decodeToString(base64Encoded); Assert.assertEquals(str,str2);
經過如上方式能夠進行 base64編碼/解碼操做dom
Stringstr = "hello"; Stringbase64Encoded = Hex.encodeToString(str.getBytes()); Stringstr2 =newString(Hex.decode(base64Encoded.getBytes())); Assert.assertEquals(str,str2);
經過如上方式能夠進行 16 進制字符串編碼/解碼操做。ide
Hash存儲網站
散列算法通常用於生成數據的摘要信息,是一種不可逆的算法,通常適合存儲密碼之類的數據,常見的散列算法如 MD五、SHA等。通常進行散列時最好提供一個 salt(鹽),好比加密密碼「admin」,產生的散列值是「21232f297a57a5a743894a0e4a801fc3」,能夠到一些 md5解密網站很容易的經過散列值獲得密碼「admin」,即若是直接對密碼進行散列相對來講破解更容易,此時咱們能夠加一些只有系統知道的干擾數據,如用戶名和 ID(即鹽);這樣散列的對象是「密碼+用戶名+ID」,這樣生成的散列值相對來講更難破解。ui
Stringstr = "hello"; Stringsalt = "123"; Stringmd5 =new Md5Hash(str, salt).toString();//還能夠轉換爲 toBase64()/toHex()
如上代碼經過鹽「123」MD5散列「hello」。另外散列時還能夠指定散列次數,如 2次表示:md5(md5(str)):「new Md5Hash(str, salt, 2).toString()」
Stringstr = "hello"; Stringsalt = "123"; Stringsha1 =new Sha256Hash(str, salt).toString();
使用 SHA256 算法生成相應的散列數據,另外還有如 SHA一、SHA512算法。
Shiro 還提供了通用的散列支持:
Stringstr = "hello"; Stringsalt = "123"; //內部使用MessageDigest StringsimpleHash =new SimpleHash("SHA-1", str, salt).toString();
經過調用 SimpleHash 時指定散列算法,其內部使用了 Java的 MessageDigest 實現。爲了方便使用,Shiro提供了 HashService,默認提供了 DefaultHashService實現
DefaultHashServicehashService =new DefaultHashService(); //默認算法 SHA-512 hashService.setHashAlgorithmName("SHA-512"); hashService.setPrivateSalt(newSimpleByteSource("123"));//私鹽,默認無 hashService.setGeneratePublicSalt(true);//是否生成公鹽,默認false hashService.setRandomNumberGenerator(new SecureRandomNumberGenerator());//用於生成公鹽。默認就這個 hashService.setHashIterations(1);//生成 Hash 值的迭代次數 HashRequestrequest =new HashRequest.Builder() .setAlgorithmName("MD5").setSource(ByteSource.Util.bytes("hello")) .setSalt(ByteSource.Util.bytes("123")).setIterations(2).build(); Stringhex =hashService.computeHash(request).toHex();
加密存儲
Shiro 還提供對稱式加密/解密算法的支持,如 AES、Blowfish等;當前尚未提供對非對稱加密/解密算法支持,將來版本可能提供。
AES 算法實現:
AesCipherServiceaesCipherService =new AesCipherService(); aesCipherService.setKeySize(128);//設置 key 長度 //生成 key Keykey=aesCipherService.generateNewKey(); Stringtext = "hello"; //加密 StringencrptText = aesCipherService.encrypt(text.getBytes(),key.getEncoded()).toHex(); //解密 Stringtext2 = newString(aesCipherService.decrypt(Hex.decode(encrptText),key.getEncoded()).getBytes()); Assert.assertEquals(text, text2);
密碼的驗證
使用「密碼的存儲」章節中其中一種存儲方式存儲密碼以後,接下來就是登錄時密碼的驗證.
該過程主要涉及到兩個問題:
(1) 讓Shiro知道數據庫中存儲的密碼是經過什麼方式存儲的?
能夠在指定比較器時進行設置
(2) 讓Shiro知道如何比對數據庫與登陸時的密碼?
Shiro主要經過CredentialsMatcher的子類來實現密碼的對比,Shiro已經實現了比較常見的Matcher,例如Md5CredentialsMatcher,Sha256CredentialsMatcher等等,若是Shiro提供的Matcher不能知足需求,還能夠自定義Matcher.
Spring集成Shiro密碼驗證明例
本小節以Spring集成Shiro爲基礎,介紹如何使用MD5對密碼進行加密,添加密碼加密功能須要通過如下三步
保存密碼
做爲密碼的數據源頭,首先要確保在密碼保存時就使用MD5加密
在Spring的Controller對應的方法中,使用如下方法保存密碼
@RequestMapping("/setting/user/add") public String addUser(@Valid User user, BindingResult result, Map<String, Object> model, @RequestParam(value = "action") String action) { String cryptedPwd = new Md5Hash(user.getPwd()).toString(); user.setPwd(cryptedPwd); userService.saveUser(user); return "redirect:/setting/user/" + user.getName(); }
配置Matcher
當數據庫中存的是MD5形式的密碼後,就要告訴shiro如何對登錄時輸入的密碼進行對比,在這裏採用默認的HashedCredentialsMatcher做爲比較器
Shiro-config.xml配置以下
<!—在自定義的Realm中配置Matcher,並指定加密算法--> <beanid="customerRealm" class="com. test.security.CustomerRealm"> <propertyname="credentialsMatcher"> <beanclass="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <propertyname="hashAlgorithmName" value="MD5" /> </bean> </property> </bean>
密碼校驗
對於CustomerRealm而言,不須要關心密碼是如何被匹配的,只須要將用戶輸入的密碼傳入AuthenticationInfo對象中便可
@Override protectedAuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { // 獲取基於用戶名和密碼的令牌,authcToken是從LoginController裏面//currentUser.login(token)傳過來的 UsernamePasswordToken token = (UsernamePasswordToken) authcToken; User user = userService.findUserByName(token.getUsername()); if(null != user) { AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getName(), user.getPwd(),user.getName()); this.setSession(GCloudConstant.CURRENT_USER,user.getName()); return authcInfo; }else { return null; } }