一.Shiro簡介
Shiro框架是和spring security框架作用差不多的一個安全認證授權框架,但它更加的輕便和簡單,越來越多的企業在使用它進行角色權限,安全認證等方面功能的管理.
二.主要架構閱覽
Subject 主體,既可以代表用戶,也可以代表程序(網絡爬蟲等),它需要訪問系統,系統則需要對其進行認證和授權.
? ? ? ? SecurityManager 安全管理,用戶請求Url,對應于一個Subject對象,由SecurityManager統一對Subject進行認證和授權.(父)
? ? ? ? Authenricator 認證部分,主要對Subject進行認證,Subject的信息在shrio中是通過AuthenticationToken對象來儲存,由Authenrication進行驗證管理.(子)
Authorizer 授權部分,Subject認證后,由它來對其授予對應角色權限.(子)
SessionManager Shiro的session管理方式,Shiro提供了一個專門管理session的方式,通常的web程序中的session是HttpSession的對象,是由web容器來管理的.
SessionDao session的接口,Shiro通過它來管理session數據,個性化的session數據儲存需要使用sessionDao.
CacheManager ?緩存管理工具,主要對session數據和授權數據進行緩存,減小數據庫的訪問壓力.可以通過和ehcache的整合對緩存數據進行管理.
Pluggable Realms 可擴展領域,相當于數據源,我們通過上面內容可以大致了解到Shiro的工作原理,但Shiro是怎樣得知Subject的信息和數據庫的信息是否匹配呢?Shiro這里就提供了一個realms的概念,它的作用就是得到數據庫中的信息.這個realm是可以多個并且可以自定義,只需繼承AuthorizingRealm這個接口就可以了.
注意:對Subject進行認證和授權都需要調用realm,所以realm不僅僅相當于數據源,更加包含了認證和授權的一種邏輯.
Cryptography ?密碼演算法,一個密碼管理工具,提供了一套加密/解密的組件.比如長用的散列,加/解密等功能,日常練習所使用的md5算法其實是一種散列算法,只能加密,不能解密.
可見Shrio的核心部分還是認證和授權部分,其他都是圍繞這倆部分進行的,只需理解了這兩部分就可以進行開發了.
三.Shiro認證實例(入門案例)
Shiro處理一個Subject流程圖
Shiro處理認證流程圖,相當于上圖的擴充和細化
可見,Shiro處理流程是一級一級的調用,主要是調用Authentication來進行驗證,最后還是需要使用realm來進行身份驗證(realm后面會提).
有了基本的執行流程圖,大概就能懂了Shiro的運行原理,接下來實踐一下,首先新建一個普通的java工程,導入shiro-core.jar包
這個作為入門程序導入這一個核心包就行了,建立測試代碼,我這里用的是junit,也可以直接main()走起.代碼如下:
//模擬用戶登錄登出
@Test
public void demo1(){
//創建SecurityManager工廠,生成SecurityManager環境(通過shrio的ini文件), Factory<SecurityManager> ?factory = new IniSecurityManagerFactory("classpath:shrio.ini");
//創建SecurityManager
SecurityManager manager= factory.getInstance();
//將當前環境設為SecurityManager
SecurityUtils.setSecurityManager(manager);
//模擬Subject
Subject subject = SecurityUtils.getSubject();
//提交認證是說攜帶的信息儲存在 token 中
UsernamePasswordToken token = new UsernamePasswordToken("Floder", "Floder");
try {
?//模擬獲取登陸
?subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//判斷是否認證通過
boolean isOk = subject.isAuthenticated();
System.out.println("用戶是否登錄:"+isOk);
//模擬用戶退出登陸
subject.logout();
isOk = subject.isAuthenticated();
System.out.println("用戶是否登錄:"+isOk);
}
這里有幾點要說明的,
1.這里需要配置Shiro的配置文件,我看了一下源碼,是通過InputStream來掃描配置文件,所以文件名任意.需要表明路徑即可,classpath即為項目的src目錄下,Shiro.ini的配置如下
#自定義數據源 格式 用戶名=密碼
[users]
Floder=Floder
2.構建工廠時Factory的泛型SecurityManager需要是用的是SecurityManager的接口,因為java.lang.SecurityManager也存在...
Shiro授權流程圖
和認證流程圖差不多,不過這個調用的是Authrizer模塊
在進行實際操作前,需要理解Shiro的兩個重要概念
1.基于角色管理:根據用戶的相應角色授予相應的資源權限,但這樣做有點不好的地方就是每新增一個角色都要對角色進行相應的資源權限管理(超級QQ,QQ會員),在實際開發中太過麻煩,不經常使用.
2.基于資源管理:對資源的訪問需要一定的權限,這樣的話每新增一個角色只需在權限對應的資源進行角色新增即可.特別方便.
測試代碼:
//模擬用戶認證授權
@Test
public void testAuthorizer(){
//創建SecurityManager工廠,生成SecurityManager環境(通過shrio的ini文件),//通過ini配置文件創建
securityManagerFactory factory = new IniSecurityManagerFactory("classpath:shrio-permission.ini");
//創建SecurityManager
SecurityManager manager= factory.getInstance();
//將當前環境設為SecurityManager
SecurityUtils.setSecurityManager(manager);
//模擬Subject
Subject subject = SecurityUtils.getSubject();
//提交認證是說攜帶的信息儲存在 Token 中
UsernamePasswordToken token = new UsernamePasswordToken("Floder", "Floder");
//模擬獲取登陸
try {
subject.login(token);
} catch (AuthenticationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//基于角色判斷
//判斷已認證用戶是否擁有role1角色
boolean isHasRole = subject.hasRole("role2");
System.out.println("判斷已認證用戶是否擁有role2角色 "+isHasRole);
//也可以判斷已認證用戶是否有多個角色
boolean isHasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2"));
System.out.println("判斷已認證用戶是否有多個角色"+isHasAllRoles);
//基于資源判斷
//判斷已認證用戶的資源是否擁有對應單個的操作
boolean isPermitted = subject.isPermitted("user:create");
System.out.println("判斷已認證用戶的資源是否擁有對應的單個操作 "+isPermitted);
//判斷已認證用戶的資源是否擁有對應的多個操作
boolean isPermittedAll = subject.isPermittedAll("user:create","user:update");
System.out.println("判斷已認證用戶的資源是否擁有對應的多個操作 "+isPermittedAll);
}
這里需要新建一個Shrio-permission.ini配置文件,具體信息如下:
#用戶對應的角色
#用戶Floder擁有角色role1和role2
[users]
Floder = Floder,role1,role2
floder=floder,role2
#角色對應的資源權限
[roles]
#權限標識符符號規則 資源:操作:實例 user:create:01 表示對用戶資源實例01進行create操作
#user:create 表示對資源進行create操作,相當于user:create:*
#user:*:01 表示對用戶資源實例01進行所有操作
role1=user:create,user:update
role2 = user:create
和Shrio認證一樣,只不過這里的user新增的角色欄,另外還對[roles]進行了相應的權限設置,看代碼即可,注釋都寫的清清楚楚
通過這兩個認證和授權實例,大概能理解了ini文件起的作用,一般是相當于數據源,在Shiro中就是Realm,注意,Realm不僅僅起數據源的作用,更加包含了認證和授權的一種邏輯.重要的事說兩遍
四.自定義Realm實現
在上一節中我們并沒有自定義realm,那它是怎樣調用認證和授權的一種邏輯呢?原來,Shiro默認提供了一個IniRealm實現,但我們知道,可以自定義Realm實現來實現我們的需求,只需實現AuthorizingRealm接口就行了(常用是這個),Realm有多種接口:
Realm結構圖
具體繼承那個接口看需求,我們測試的時候就使用AuthorizingRealm
自定義UserRealm:
//設置realm名,需要繼承setName()方法
@Override
public void setName(String name) {
super.setName("userName");
}
//認證方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//得到token的用戶信息
String userId = (String) token.getPrincipal();
//模擬數據庫返回數據,如果不存在就返回null
//.......
//模擬數據庫返回數據,存在就返回一個credentials
String password = "Floder";
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userId, password, this.getName());
return info;
}
//授權方法(依賴于上面的認證信息)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
String userId = (String) principals.getPrimaryPrincipal();
//模擬從數據庫得來的角色信息,
List<String> listPermission = new ArrayList<String>();
//模擬用戶創建權限
listPermission.add("user:create");
//模擬用戶添加權限
listPermission.add("user:update");
//這里的info使用的是SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//SimpleAuthorizationInfo攜帶的是數據庫返回的信息,數據實現的是collection接口的玩意info.addStringPermissions(listPermission);
return info;
}
? 這里如果需要模擬null就很簡單了,寫個if()判斷即可,這里的info信息我們實現的是SimpleAuthenticationInfo接口和SimpleAuthenticationInfo授權接口,
? ? ? 我們知道realm是與數據庫打交道的,由于有了自定義realm,我們就不用在ini文件中寫[users]了,所以修改Shiro.ini文件的信息:
[main]
#聲明我們自定義的realm
userRealm=com.floder.junit.realm.UserRealm
#將realm放入SecurityManager中
SecurityManager.realms=$userRealm
好了,準備工作都完成了,接下來開始測試,認證登陸模擬和上一節模擬登陸代碼相同,授權測試代碼需要修改部分代碼:
try {
//模擬獲取登陸
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
//基于資源判斷
//判斷已認證用戶的資源是否擁有對應的操作
boolean isPermitted = subject.isPermitted("user:create");
System.out.println("判斷已認證用戶的資源是否擁有對應的操作 "+isPermitted);
//使用check方法進行授權,如果不通過則拋出異常
subject.checkPermission("item:add");//這是需要添加的,基于角色就不需要測試了
注釋這么清楚,就不解釋了.