一、Shiro介紹:
1、什麼是shiro:
(1)shiro是apache的一個開源架構,是一個權限管理的架構,實作使用者認證、使用者授權。
(2)spring中有spring security,是一個權限架構,但是它和spring依賴過于緊密,沒有shiro使用簡單。shiro不依賴于spring,shiro不僅可以實作 web應用的權限管理,還可以實作c/s系統,分布式系統權限管理,shiro屬于輕量架構,越來越多企業項目開始使用shiro。
(3)使用shiro實作系統 的權限管理,有效提高開發效率,進而降低開發成本。
2、shiro架構:
(1)subject:主體,可以是使用者也可以是程式,主體要通路系統,系統需要對主體進行認證、授權。
(2)securityManager:安全管理器,主體進行認證和授權都 是通過securityManager進行。
(3)authenticator:認證器,主體進行認證最終通過authenticator進行的。
(4)authorizer:授權器,主體進行授權最終通過authorizer進行的。
(5)sessionManager:web應用中一般是用web容器對session進行管理,shiro也提供一套session管理的方式。
(6)SessionDao: 通過SessionDao管理session資料,針對個性化的session資料存儲需要使用sessionDao。
(7)cache Manager:緩存管理器,主要對session和授權資料進行緩存,比如将授權資料通過cacheManager進行緩存管理,和ehcache整合對緩存資料進行管理。
(8)realm:域,領域,相當于資料源,通過realm存取認證、授權相關資料。在realm中存儲授權和認證的邏輯。
(9)cryptography:密碼管理,提供了一套加密/解密的元件,友善開發。比如提供常用的散列、加/解密等功能。比如 md5雜湊演算法。
3、相關jar包依賴:
與其它java開源架構類似,将shiro的jar包加入項目就可以使用shiro提供的功能了。shiro-core是核心包必須選用,還提供了與web整合的shiro-web、與spring整合的shiro-spring、與任務排程quartz整合的shiro-quartz等
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.4.0</version>
</dependency>
也可以通過引入shiro-all包括shiro所有的包:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
</dependency>
二、Shiro認證入門程式搭建:
1、shiro認證流程:
2、導入jar包依賴:shiro-core.jar
3、工程結構:
4、編寫shiro-first.ini配置檔案:
通過此配置檔案建立securityManager工廠。
#對使用者資訊進行配置
[users]
#使用者賬戶和密碼
zhangsan=111111
lisi=222222
5、Authentication類:
//shiro入門程式測試類:
public class Authentication {
//使用者登陸和退出測試
@Test
public void testLogin(){
//1、建立securityManager工廠,通過ini配置檔案建立securityManager工廠
Factory<SecurityManager> factory=
new IniSecurityManagerFactory("classpath:shiro-first.ini");
//2、建立securityManager
SecurityManager securityManager=factory.getInstance();
//3、将SecurityManager設定在目前運作環境中
SecurityUtils.setSecurityManager(securityManager);
//4、從SecurityUtils裡面 建立一個subject;
Subject subject=SecurityUtils.getSubject();
//5、在認證送出前準備token(令牌)
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","111111");
try{
//6、執行認證送出;
subject.login(token);
}catch (AuthenticationException e) {
e.printStackTrace();
}
//7、是否認證通過
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
//8、退出操作
subject.logout();
isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
}
}
6、運作結果:
至此,一個簡單的shiro入門程式就搭建完成了。
三、shiro的執行流程:
1、通過ini配置檔案建立securityManager;
2、調用subject.login方法主體送出認證,送出的token;
3、securityManager進行認證,securityManager最終由ModularRealmAuthenticator進行認證;
4、ModularRealmAuthenticator調用IniRealm(給realm傳入token) 去ini配置檔案中查詢使用者資訊;
5、IniRealm根據輸入的token(UsernamePasswordToken)從 shiro-first.ini查詢使用者資訊,根據賬号查詢使用者資訊(賬号和密碼):
(1)如果查詢到使用者資訊,就給ModularRealmAuthenticator傳回使用者資訊(賬号和密碼)
(2)如果查詢不到,就給ModularRealmAuthenticator傳回null
6、ModularRealmAuthenticator接收IniRealm傳回Authentication認證資訊
(1)如果傳回的認證資訊是null,ModularRealmAuthenticator抛出異常(org.apache.shiro.authc.UnknownAccountException)
(2)如果傳回的認證資訊不是null(說明inirealm找到了使用者),對IniRealm傳回使用者密碼 (在ini檔案中存在)和 token中的密碼 進行對比,如果不一緻抛出異常(org.apache.shiro.authc.IncorrectCredentialsException)
小結:
ModularRealmAuthenticator的作用是進行認證,需要調用realm查詢使用者資訊(在資料庫中存在使用者資訊),
ModularRealmAuthenticator進行密碼對比(認證過程)。
realm:需要根據token中的身份資訊去查詢資料庫(入門程式使用ini配置檔案),如果查到使用者傳回認證資訊,如果查詢不到傳回null。
四、自定義Reaml進行使用者認證:
實際開發需要realm從資料庫中查詢使用者資訊。
1、繼承realm接口:
2、自定義realm的示例:
工程結構:
3、自定義realm:
//自定義的Realm,需要繼承AuthorizingRealm
public class CustomRealm extends AuthorizingRealm{
// 設定realm的名稱
@Override
public void setName(String name) {
super.setName("customRealm");
}
// 用于認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// token是使用者輸入的
// 第一步從token中取出身份資訊
String userCode = (String) token.getPrincipal();
// 第二步:根據使用者輸入的userCode從資料庫查詢
// ....
// 如果查詢不到傳回null
//這個例子中假設資料庫中使用者賬号是zhangsansan
if(!userCode.equals("zhangsan")){//
return null;
}
// 模拟從資料庫查詢到密碼是111111
String password = "111111";
// 如果查詢到傳回認證資訊AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, this.getName());
return simpleAuthenticationInfo;
}
// 用于授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// TODO Auto-generated method stub
return null;
}
}
4、配置realm:
需要在shiro-realm.ini配置realm注入到securityManager中:
[main]
#自定義的realm
customRealm=com.zwp.shiro.realm.CustomRealm
#将realm設定到securityManager,相當于spring中的注入
securityManager.realms=$customRealm
5、測試:
@Test
public void testCustomRealm() {
//1、建立securityManager工廠,通過ini配置檔案建立securityManager工廠
Factory<SecurityManager> factory=
new IniSecurityManagerFactory("classpath:shiro-realm.ini");
//2、建立securityManager
SecurityManager securityManager=factory.getInstance();
//3、将SecurityManager設定在目前運作環境中
SecurityUtils.setSecurityManager(securityManager);
//4、從SecurityUtils裡面 建立一個subject;
Subject subject=SecurityUtils.getSubject();
//5、在認證送出前準備token(令牌)
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","111111");
try{
//6、執行認證送出;
subject.login(token);
}catch (AuthenticationException e) {
e.printStackTrace();
}
//7、是否認證通過
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
//8、退出操作
subject.logout();
isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
}
6、測試結果:
至此,Realm的配置就完成了。
五、Shiro的MD5加密算法:
1、雜湊演算法:
在項目中,通常需要對密碼進行散列,常用的有MD5、SHA。
(1)對md5密碼,如果知道散列後的值可以通過窮舉算法,得到md5密碼對應的明文。是以,建議對md5進行散列時加salt(鹽),進行加密相當于對原始密碼+鹽進行散列。
(2)正常使用時散列方法:
在程式中對原始密碼+鹽進行散列,将散列值存儲到資料庫中,并且還要将鹽也要存儲在資料庫中。
(3)如果進行密碼對比時,使用相同方法,将原始密碼+鹽進行散列,進行比對。
2、MD5散列測試程式:
public class MD5Test {
public static void main(String[] args) {
//原始 密碼
String source = "111111";
//鹽
String salt = "qwerty";
//散列次數
int hashIterations = 2;
//上邊散列1次:f3694f162729b7d0254c6e40260bf15c
//上邊散列2次:36f2dfa24d0a9fa97276abbe13e596fc
//構造方法中:
//第一個參數:明文,原始密碼
//第二個參數:鹽,通過使用随機數
//第三個參數:散列的次數,比如散列兩次,相當 于md5(md5(''))
Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
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支援雜湊演算法:
需求:實際開發時,realm要進行MD5值(明文散列後的值)的對比;
(1)項目結構:
(2)建立realm:(CustomRealmMd5.java)
//自定義realm支援雜湊演算法:
public class CustomRealmMd5 extends AuthorizingRealm {
// 設定realm的名稱
@Override
public void setName(String name) {
super.setName("customRealmMd5");
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// token是使用者輸入的
// 第一步從token中取出身份資訊
String userCode = (String) token.getPrincipal();
// 第二步:根據使用者輸入的userCode從資料庫查詢
// ....
// 如果查詢不到傳回null
// 資料庫中使用者賬号是zhangsansan
if(!userCode.equals("zhangsan")){
return null;
}
// 模拟從資料庫查詢到密碼,散列值
String password = "13f79dafcbbedc313273e2b891ac84d3";
// 從資料庫擷取salt
String salt = "qwerty";
//上邊散列值和鹽對應的明文:123456 散列次數2
// 如果查詢到傳回認證資訊AuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
userCode, password, ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
(3)在realm的ini檔案中配置憑證比對器:
[main]
#定義憑證比對器:
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#雜湊演算法:
credentialsMatcher.hashAlgorithmName=md5
#散列次數:
credentialsMatcher.hashIterations=2
#将憑證比對器設定到realm
customRealm=com.zwp.shiro.realm.CustomRealmMd5
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm
(4)測試類:
//自定義realm支援雜湊演算法測試:
@Test
public void testCustomRealmMd5() {
//1、建立securityManager工廠,通過ini配置檔案建立securityManager工廠
Factory<SecurityManager> factory=
new IniSecurityManagerFactory("classpath:shiro-realm-md5.ini");
//2、建立securityManager
SecurityManager securityManager=factory.getInstance();
//3、将SecurityManager設定在目前運作環境中
SecurityUtils.setSecurityManager(securityManager);
//4、從SecurityUtils裡面 建立一個subject;
Subject subject=SecurityUtils.getSubject();
//5、在認證送出前準備token(令牌)
UsernamePasswordToken token=new UsernamePasswordToken("zhangsan","123456");
try{
//6、執行認證送出;
subject.login(token);
}catch (AuthenticationException e) {
e.printStackTrace();
}
//7、是否認證通過
boolean isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
//8、退出操作
subject.logout();
isAuthenticated = subject.isAuthenticated();
System.out.println("是否認證通過:"+isAuthenticated);
}
至此,自定義realm支援雜湊演算法的就完成了。