shiro關於認證的學習
一、shiro認證入門程式工程
shiro分為認證和授權,這裡我們先學習shiro的認證。
1、shiro認證流程
2、shiro認證入門工程的搭建
(1)首先建立一個java工程
在eclipse中顯示的工程結構
(2)新增shiro的jar包
最核心的jar包就是:shiro-core.jar(下邊其他的是需要依賴的jar包)
在工程中建立一個lib資料夾(Folder),把jar包拷貝進去,然後build path。
在eclipse中顯示的工程結構
這裡我也貼一下,用maven時候,pom檔案中的座標
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.3</version> </dependency> 也可以通過引入shiro-all包括shiro所有的包: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency>
(3)建立一個log4j的日誌檔案
建立一個config資料夾(Source Folder),把log4j的日誌檔案貼進去。
這裡可以簡短的貼一下log4j日誌檔案最基本的內容
log4j.rootLogger=debug,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
到時候建立一個log4j.properties檔案,把上邊的貼入進去就可以了。
在eclipse中顯示的工程結構
這樣我們這個最入門的工程就搭建好了。
3、開始寫shiro測試Demo
首先我們建立一個包com.shiro.authentication,建立一個測試類AnthenticationTest.java。
我們在測試這個環境中,並沒有連線資料庫,而是建立一個ini檔案,在測試執行的時候載入ini檔案裡邊配置的內容,就相當於從資料庫中比對了。(ini檔案相對於properties檔案,就是可以分組,而properties檔案只是簡單的key,value形式)
ini檔案配置的內容
(ini配置檔案放在config資料夾中)
接下來是測試類的內容
package com.shiro.authentication;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;
/**
*
* <p>Title: AnthenticationTest</p>
* <p>Description: 認證測試</p>
* <p>Company: www.zonsim.com</p>
* @author Liuyl
* @date 2016年10月5日上午12:14:55
* @version 1.0
*/
public class AnthenticationTest {
// 使用者登陸和退出
@Test
public void testLoginAndLogout(){
/**
* (1)構建securityManager環境
*/
// 建立securityManager工廠,通過ini配置檔案建立securityManager工廠(ini是window中的一種配置檔案)
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:shiro-first.ini");
//在ini裡進行的配置,SecurityManager建立的時候就把ini裡邊的資訊載入進來了,就相當於查詢資料庫了
//在說明一些SecurityManager類要匯入org.apache.shiro.mgt.SecurityManager;包
// 建立SecurityManager
SecurityManager securityManager = factory.getInstance();
//通過建立SecurityManager工廠和建立SecurityManager物件,完成了第一步構建securityManager環境
/**
* 這個算是中間銜接的過程
*/
// 將securityManager設定當前的執行環境中(這裡是單例裡的,但是不用我們管理,SecurityUtils完成這件事)
SecurityUtils.setSecurityManager(securityManager);
/**
* 現在是一個測試環境,需要模擬一個subject,這裡需要我們構造出來。
* 正常情況下,這個subject不需要我們來管,到時候就是我的一個http請求就是subject
*/
// 從SecurityUtils裡邊建立一個subject
Subject subject = SecurityUtils.getSubject();
/**
* (2)執行認證提交
*/
// 在認證提交前準備token(令牌)
// 這裡的賬號和密碼 將來是由使用者輸入進去
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
"111111");
//我們在認證的時候可以丟擲一個異常
try {
//認證提交
subject.login(token);
} catch (AuthenticationException e) {//AuthenticationException:shiro自己的認證異常
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* (3)得到一個驗證的結果
*/
// 是否認證通過
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:" + isAuthenticated);
// 退出操作
subject.logout();//一退出,身份資訊就沒有了
// 是否認證通過
isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:" + isAuthenticated);//退出了就因該是false了
/**
* 整體的流程就是token中的使用者名稱密碼要和ini配置檔案裡邊的使用者名稱密碼進行比對,
* 成功了就是true,沒有就是false
*/
}
}
4、測試結果說明
(1)上邊測試的就過是true,false。
在執行程式的時候,會拿著UsernamePasswordToken產生的token,去對面ini配置檔案裡邊定義的帳號密碼。有就返回true。
當退出之後,身份資訊就沒有了,在進行身份認證,就是返回false了。
(2)如果使用者名稱不對
會報帳號不存在異常:org.apache.shiro.authc.UnknownAccountException
返回結果則是false,false
(3)如果密碼不正確
會報憑證錯誤異常:org.apache.shiro.authc.IncorrectCredentialsException
返回結果則是false,false
5、總結shrio認證執行流程
@1.通過ini配置檔案建立securityManager。
@2.呼叫subject.login方法主體提交認證,提交的token。
@3.securityManager進行認證,securityManager通過Authenticator介面,最終由(呼叫)ModularRealmAuthenticator進行認證。
@4.ModularRealmAuthenticator呼叫IniRealm(給realm傳入的token)去ini配置檔案中查詢使用者資訊。
@5.IniRealm根據輸入的token(UsernamePasswordToken)從shiro-first.ini(自己定義的ini檔案)查詢使用者資訊,根據賬號查詢使用者資訊(賬號和密碼)
如果查詢到使用者資訊,就給ModularRealmAuthenticator返回使用者資訊(賬號和密碼),
如果查詢不到,就給ModularRealmAuthenticator返回null(說明賬號不存在)。
@6.ModularRealmAuthenticator接收IniRealm返回Authentication認證資訊,
如果返回的認證資訊是null,ModularRealmAuthenticator丟擲異常(org.apache.shiro.authc.UnknownAccountException)。
如果返回的認證資訊不是null(說明inirealm找到了使用者),ModularRealmAuthenticator對IniRealm返回使用者密碼(在ini檔案中存在)和token中的密碼進行對比,如果不一致丟擲異常(org.apache.shiro.authc.IncorrectCredentialsException)
二、自定義reaml認證
將來實際開發需要realm從資料庫中查詢使用者資訊。
1、在專案中建立一個realm包com.shiro.realm
2、開始建立自定義realm類
這個自定義realm類一般繼承AuthorizingRealm這個類。
下面就是自定義realm類
package com.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
*
* <p>Title: CustomRealm</p>
* <p>Description: 自定義realm</p>
* <p>Company: www.zonsim.com</p>
* @author Liuyl
* @date 2016年10月5日下午3:47:58
* @version 1.0
*/
public class CustomRealm extends AuthorizingRealm{
// 設定realm的名稱(這裡邊就讀取ini配置檔案中的資料)
@Override
public void setName(String name) {
super.setName("customRealm");
}
/**
* 需要實現AuthorizingRealm這個介面的兩個方法
*/
//使用者認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//因為我們現在還不連線資料庫,我們在這裡模擬一下
//第一步從token中取出身份資訊( token是使用者輸入的)(這裡邊userCode取到的值是前邊測試類中UsernamePasswordToken設定的值)
String userCode = (String) token.getPrincipal();
// 第二步:根據使用者輸入的userCode從資料庫查詢
// ....
//下邊這步是模擬沒有找到使用者時候的情況
// 如果查詢不到返回null
//資料庫中使用者賬號是zhangsansan
if(!userCode.equals("zhangsansan")){//
return null;
}
// 模擬從資料庫查詢到密碼
String password = "111111";
// 如果查詢到返回認證資訊AuthenticationInfo
//引數1.身份資訊 2.憑證3.realm的名稱
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, this.getName());
return simpleAuthenticationInfo;
}
//使用者授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
// TODO Auto-generated method stub
return null;
}
}
3、配置自定義realm
自定義reaml定義好之後,要對realm進行配置,如果不配置realm,securityManager就不知道呼叫哪個realm。
所以需要在shiro-realm.ini(也可以配置到shiro-first.ini)配置realm注入到securityManager中。
ini配置檔案的內容為:
4、寫測試類進行測試
就是上邊測試類,改變一下載入的ini配置檔名稱,不載入shiro-first.ini,改為載入shiro-realm.ini
這樣完成了自定義realm的認證方式。
以後的開發中都會用自定義realm的方式來寫。
最後測試的結果,同上邊的入門工程測試結果一樣。
三、自定義realm支援雜湊演算法的認證
1、首先了解一下加密的雜湊演算法
通常需要對密碼進行雜湊,常用的有md5、sha,(md5雜湊後就不能逆向了)。
對md5密碼,如果知道雜湊後的值可以通過窮舉演算法,得到md5密碼對應的明文。
建議對md5進行雜湊時加salt(鹽),進行加密相當於對原始密碼+鹽進行雜湊。
正常使用時雜湊方法:
在程式中對原始密碼+鹽進行雜湊,將雜湊值儲存到資料庫中,並且還要將鹽也要儲存在資料庫中。
如果進行密碼對比時,使用相同方法,將原始密碼+鹽進行雜湊,進行比對。
2、先來一個加密的測試程式,繼續瞭解一下
package com.shiro.authentication;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.junit.Test;
/**
*
* <p>Title: MD5Test</p>
* <p>Description: </p>
* <p>Company: www.zonsim.com</p>
* @author Liuyl
* @date 2016年10月5日下午4:36:30
* @version 1.0
*/
public class MD5Test {
@Test
public void MD5_Test(){
//原始 密碼
String source = "111111";
//鹽
String salt = "qwerty";
//雜湊次數
int hashIterations = 2;
//上邊雜湊1次:f3694f162729b7d0254c6e40260bf15c
//上邊雜湊2次:36f2dfa24d0a9fa97276abbe13e596fc
//構造方法中:
//第一個引數:明文,原始密碼
//第二個引數:鹽,通過使用隨機數
//第三個引數:雜湊的次數,比如雜湊兩次,相當 於md5(md5(''))
Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
//得到md5雜湊後的密碼
String password_md5 = md5Hash.toString();
System.out.println(password_md5);
//第一個引數:雜湊演算法 (這個和上邊的作用是一樣的)
SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
System.out.println(simpleHash.toString());
}
}
3、接下來,寫最重要的支援雜湊演算法的自定義realm
package com.shiro.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
/**
*
* <p>Title: CustomRealm</p>
* <p>Description: 自定義realm</p>
* <p>Company: www.zonsim.com</p>
* @author Liuyl
* @date 2016年10月5日下午3:47:58
* @version 1.0
*/
public class CustomRealmMD5 extends AuthorizingRealm{
// 設定realm的名稱
@Override
public void setName(String name) {
super.setName("customRealmMD5");
}
/**
* 需要實現AuthorizingRealm這個介面的兩個方法
*/
//使用者認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//因為我們現在還不連線資料庫,我們在這裡模擬一下
//第一步從token中取出身份資訊( token是使用者輸入的)(這裡邊userCode取到的值是前邊測試類中UsernamePasswordToken設定的值)
String userCode = (String) token.getPrincipal();
// 第二步:根據使用者輸入的userCode從資料庫查詢
// ....
/*//下邊這步是模擬沒有找到使用者時候的情況
// 如果查詢不到返回null
//資料庫中使用者賬號是zhangsansan
if(!userCode.equals("zhangsansan")){//
return null;
}*/
// 模擬從資料庫查詢到密碼,進行雜湊後的值(原來的明文密碼是111111)
String password = "f3694f162729b7d0254c6e40260bf15c";
// 從資料庫獲取salt
String salt = "qwerty";
// 如果查詢到返回認證資訊AuthenticationInfo
//引數1.身份資訊 2.憑證3.鹽4.realm的名稱
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
//使用者授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
// TODO Auto-generated method stub
return null;
}
}
4、同上,需要配置自定義realm
這裡需要把憑證匹配器和自定義的realm都定義到realm中。
ini配置檔案的內容為:
5、寫測試類進行認證測試
就是上邊測試類,改變一下載入的ini配置檔名稱,不載入shiro-realm.ini,改為載入shiro-realm-md5.ini
這種方式基本上都用這種。
最後測試的結果,同上邊的入門工程測試結果一樣。
這樣關於shiro認證方面的使用方法和知識點都介紹完成了。