《跟我學Shiro》學習筆記 第四章:編碼/加密
前言
密碼儲存應該採用加密/生產密碼摘要儲存。而不是採用明文儲存,這期我們就來學習一下Shiro在編碼與加密方面的功能。
4.1編碼/解碼
Shiro提供了base64和16進位制字串編碼/解碼的API支援,方便一些編碼解碼操作。Shiro內部的一些資料的儲存/表示都使用了base64和16進位制字串。
@Test
public void base64encode(){
String str = "hello";
String base64Encoded = Base64.encodeToString(str.getBytes());
log.info("編碼後:"+base64Encoded);
String str2 = Base64.decodeToString(base64Encoded);
log.info("解碼後:"+str2);
}
通過如上方式可以進行base64編碼/解碼操作。
@Test
public void hexEncode(){
String str = "hello";
String base64Encoded = Hex.encodeToString(str.getBytes());
log.info("編碼後:"+base64Encoded);
String str2 = new String(Hex.decode(base64Encoded.getBytes()));
log.info("解碼後:"+str2);
}
通過如上方式可以進行16進位制字串編碼/解碼操作。
4.2雜湊演算法
雜湊演算法一般用於生成資料的摘要資訊,是一種不可逆的演算法,一般適合儲存密碼之類的資料,常見的雜湊演算法如MD5、SHA等。一般進行雜湊時最好提供一個salt(鹽),比如加密密碼“admin”,產生的雜湊值是“21232f297a57a5a743894a0e4a801fc3”,可以到一些md5解密網站很容易的通過雜湊值得到密碼“admin”,即如果直接對密碼進行雜湊相對來說破解更容易,此時我們可以加一些只有系統知道的干擾資料,如使用者名稱和ID(即鹽);這樣雜湊的物件是“密碼+使用者名稱+ID”,這樣生成的雜湊值相對來說更難破解。
@Test
public void md5(){
String str = "hello";
String salt = "123";
String md5 = new Md5Hash(str,salt).toString();
log.info(md5);
}
md5演算法雜湊。
@Test
public void sha256(){
String str = "hello";
String salt = "123";
String sha256 = new Sha256Hash(str,salt).toString();
log.info(sha256);
}
sha256雜湊演算法。
Shiro還提供了通用的雜湊支援:
@Test
public void messageDigest(){
String str = "hello";
String salt = "123";
//內部使用MessageDigest
String simpleHash = new SimpleHash("SHA-1", str, salt).toString();
log.info(simpleHash);
}
通過呼叫SimpleHash時指定雜湊演算法,其內部使用了Java的MessageDigest實現。
為了方便使用,Shiro提供了HashService,預設提供了DefaultHashService實現。
@Test
public void testHashService() {
//預設演算法SHA-512
DefaultHashService hashService = new DefaultHashService();
hashService.setHashAlgorithmName("SHA-512");
//私鹽,預設無
hashService.setPrivateSalt(new SimpleByteSource("123"));
//是否生成公鹽,預設false
hashService.setGeneratePublicSalt(true);
//用於生成公鹽。預設就這個
hashService.setRandomNumberGenerator(new SecureRandomNumberGenerator());
//生成Hash值的迭代次數
hashService.setHashIterations(1);
HashRequest request = new HashRequest.Builder()
.setAlgorithmName("MD5").setSource(ByteSource.Util.bytes("hello"))
.setSalt(ByteSource.Util.bytes("123")).setIterations(2).build();
String hex = hashService.computeHash(request).toHex();
log.info(hex);
}
- 首先建立一個DefaultHashService,預設使用SHA-512演算法;
- 可以通過hashAlgorithmName屬性修改演算法;
- 可以通過privateSalt設定一個私鹽,其在雜湊時自動與使用者傳入的公鹽混合產生一個新鹽;
- 可以通過generatePublicSalt屬性在使用者沒有傳入公鹽的情況下是否生成公鹽;
- 可以設定randomNumberGenerator用於生成公鹽;
- 可以設定hashIterations屬性來修改預設加密迭代次數;
- 需要構建一個HashRequest,傳入演算法、資料、公鹽、迭代次數。
4.3加密/解密
Shiro還提供對稱式加密/解密演算法的支援,如AES、Blowfish等;當前還沒有提供對非對稱加密/解密演算法支援,未來版本可能提供。
AES演算法實現:
@Test
public void aes() {
AesCipherService aesCipherService = new AesCipherService();
//設定key長度
aesCipherService.setKeySize(128);
//生成key
Key key = aesCipherService.generateNewKey();
String text = "hello";
//加密
String encrptText = aesCipherService.encrypt(text.getBytes(), key.getEncoded()).toHex();
log.info(encrptText);
//解密
String text2 = new String(aesCipherService.decrypt(Hex.decode(encrptText), key.getEncoded()).getBytes());
log.info(text2);
}
4.4PasswordService/CredentialsMatcher
Shiro提供了PasswordService及CredentialsMatcher用於提供加密密碼及驗證密碼服務。
public interface PasswordService {
//輸入明文密碼得到密文密碼
String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
}
public interface CredentialsMatcher {
//匹配使用者輸入的token的憑證(未加密)與系統提供的憑證(已加密)
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
Shiro預設提供了PasswordService實現DefaultPasswordService;CredentialsMatcher實現PasswordMatcher及HashedCredentialsMatcher(更強大)。
DefaultPasswordService配合PasswordMatcher實現簡單的密碼加密與驗證服務
-
定義Realm
public class MyRealm extends AuthorizingRealm { private PasswordService passwordService; public void setPasswordService(PasswordService passwordService){ this.passwordService = passwordService; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { return new SimpleAuthenticationInfo("zhang",passwordService.encryptPassword("123"),getName()); } }
為了方便,直接注入一個passwordService來加密密碼,實際使用時需要在Service層使用passwordService加密密碼並存到資料庫。
-
ini配置(shiro-passwordservice.ini)
[main] passwordService=org.apache.shiro.authc.credential.DefaultPasswordService hashService=org.apache.shiro.crypto.hash.DefaultHashService passwordService.hashService=$hashService hashFormat=org.apache.shiro.crypto.hash.format.Shiro1CryptFormat passwordService.hashFormat=$hashFormat hashFormatFactory=org.apache.shiro.crypto.hash.format.DefaultHashFormatFactory passwordService.hashFormatFactory=$hashFormatFactory passwordMatcher=org.apache.shiro.authc.credential.PasswordMatcher passwordMatcher.passwordService=$passwordService myRealm=com.zhaojun.shiro.chapter4.realm.MyRealm myRealm.passwordService=$passwordService myRealm.credentialsMatcher=$passwordMatcher securityManager.realms=$myRealm
- passwordService使用DefaultPasswordService,如果有必要也可以自定義;
- hashService定義雜湊密碼使用的HashService,預設使用DefaultHashService(預設SHA-256演算法);
- hashFormat用於對雜湊出的值進行格式化,預設使用Shiro1CryptFormat,另外提供了Base64Format和HexFormat,對於有salt的密碼請自定義實現ParsableHashFormat然後把salt格式化到雜湊值中;
- hashFormatFactory用於根據雜湊值得到雜湊的密碼和salt;因為如果使用如SHA演算法,那麼會生成一個salt,此salt需要儲存到雜湊後的值中以便之後與傳入的密碼比較時使用;預設使用DefaultHashFormatFactory;
- passwordMatcher使用PasswordMatcher,其是一個CredentialsMatcher實現;
- 將credentialsMatcher賦值給myRealm,myRealm間接繼承了AuthenticatingRealm,其在呼叫getAuthenticationInfo方法獲取到AuthenticationInfo資訊後,會使用credentialsMatcher來驗證憑據是否匹配,如果不匹配將丟擲IncorrectCredentialsException異常。
HashedCredentialsMatcher實現密碼驗證服務
Shiro提供了CredentialsMatcher的雜湊實現HashedCredentialsMatcher,和之前的PasswordMatcher不同的是,它只用於密碼驗證,且可以提供自己的鹽,而不是隨機生成鹽,且生成密碼雜湊值的演算法需要自己寫,因為能提供自己的鹽。
-
生成密碼雜湊值
此處我們使用MD5演算法,“密碼+鹽(使用者名稱+隨機數)”的方式生成雜湊值:
String algorithmName = "md5"; String username = "liu"; String password = "123"; String salt1 = username; String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex(); int hashIterations = 2; SimpleHash hash = new SimpleHash(algorithmName, password, salt1 + salt2, hashIterations); String encodedPassword = hash.toHex();
如果要寫使用者模組,需要在新增使用者/重置密碼時使用如上演算法儲存密碼,將生成的密碼及salt2存入資料庫(因為我們的雜湊演算法是:md5(md5(密碼+username+salt2)))。
-
生成Realm
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //使用者名稱及salt1 String username = "liu"; //加密後的密碼 String password = "202cb962ac59075b964b07152d234b70"; String salt2 = "202cb962ac59075b964b07152d234b70"; //鹽是使用者名稱+隨機數 SimpleAuthenticationInfo ai = new SimpleAuthenticationInfo(username, password, getName()); ai.setCredentialsSalt(ByteSource.Util.bytes(username+salt2)); return ai; }
如果使用JdbcRealm,需要修改獲取使用者資訊(包括鹽)的sql:“select password, password_salt from users where username = ?”,而我們的鹽是由username+password_salt組成,所以需要通過如下ini配置修改:
jdbcRealm.saltStyle=COLUMN jdbcRealm.authenticationQuery=select password, concat(username,password_salt) from users where username = ? jdbcRealm.credentialsMatcher=$credentialsMatcher
1、saltStyle表示使用密碼+鹽的機制,authenticationQuery第一列是密碼,第二列是鹽;
2、通過authenticationQuery指定密碼及鹽查詢SQL;
張開濤的部落格:http://jinnianshilongnian.iteye.com/category/305053
相關文章
- shiro 學習筆記筆記
- 彙編學習筆記筆記
- PHP 資料加密 (學習筆記)PHP加密筆記
- 商密學習-分組密碼加密模式筆記密碼加密模式筆記
- 【編譯openjdk學習筆記】編譯JDK筆記
- Shiro學習筆記(一) 基本概念與使用筆記
- webpack 學習筆記:實戰之 babel 編碼Web筆記Babel
- 《Shell指令碼學習指南》學習筆記指令碼筆記
- Java_EE企業級開發學習筆記——spring學習筆記第四章Java筆記Spring
- 彙編基礎學習筆記筆記
- Python3學習筆記-字串和編碼Python筆記字串
- Linux下的makefile編寫 ——陳皓《跟我一起寫Makefile》學習筆記(二)Linux筆記
- Retrofit原始碼學習筆記原始碼筆記
- Shell指令碼學習筆記指令碼筆記
- python 學習筆記第四章:基本概念Python筆記
- Android SDK編寫學習筆記Android筆記
- ZYNQ學習筆記(一): uboot 編譯筆記boot編譯
- XML學習筆記(一):關於字元編碼的理解XML筆記字元
- 彙編學習筆記07——BCD碼及調整指令筆記
- 學習筆記1——數字基帶訊號編碼筆記
- numpy的學習筆記\pandas學習筆記筆記
- 可搜尋加密技術 - 學習筆記(一)加密筆記
- 【學習筆記】初次學習斜率最佳化的程式碼及筆記筆記
- 【學習筆記】數學筆記
- vue原始碼學習筆記1Vue原始碼筆記
- Python學習筆記—程式碼Python筆記
- jQuery原始碼學習筆記一jQuery原始碼筆記
- 學習筆記 sync/RWMutex原始碼筆記Mutex原始碼
- shell指令碼學習筆記-1指令碼筆記
- Django學習筆記—驗證碼Django筆記
- 流媒體學習筆記----配置一個編碼程式 (轉)筆記
- 彙編學習筆記之轉移指令筆記
- IT學習筆記筆記
- 學習筆記筆記
- 《JAVA學習指南》學習筆記Java筆記
- PHP 原始碼加密學習PHP原始碼加密
- 《會計學》學習筆記筆記
- iOS逆向學習筆記 - 彙編(一) - 初識彙編iOS筆記