shiro 安全框架 詳解

---恢復內容開始---前端

Shiro 簡介web

簡介
• Apache Shiro 是 Java 的一個安全(權限)框架。
• Shiro 能夠很是容易的開發出足夠好的應用,其不只能夠用在JavaSE 環境,也能夠用在 JavaEE 環境。
• Shiro 能夠完成:認證、受權、加密、會話管理、與Web 集成、緩存等。
• 下載:http://shiro.apache.org/數據庫


功能簡介apache

• 基本功能點以下圖所示:編程


功能簡介
• Authentication:身份認證/登陸,驗證用戶是否是擁有相應的身份;
• Authorization:受權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能進行什麼操做,如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具備某個權限;
• Session Manager:會話管理,即用戶登陸後就是一次會話,在沒有退出以前,它的全部信息都在會話中;會話能夠是普通 JavaSE 環境,也能夠是 Web 環境的;
• Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
• Web Support:Web 支持,能夠很是容易的集成到Web 環境;
• Caching:緩存,好比用戶登陸後,其用戶信息、擁有的角色/權限沒必要每次去查,這樣能夠提升效率;
• Concurrency:Shiro 支持多線程應用的併發驗證,即如在一個線程中開啓另外一個線程,能
• 把權限自動傳播過去;
• Testing:提供測試支持;
• Run As:容許一個用戶僞裝爲另外一個用戶(若是他們容許)的身份進行訪問;
• Remember Me:記住我,這個是很是常見的功能,即一次登陸後,下次再來的話不用登陸了api


Shiro 架構 (Shiro外部來看)瀏覽器

• 從外部來看Shiro ,即從應用程序角度的來觀察如何使用 Shiro 完成工做:緩存


Shiro 架構tomcat

• Subject:應用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外API 核心就是 Subject。Subject 表明了當前「用戶」, 這個用戶不必定是一個具體的人,與當前應用交互的任何東西都是 Subject,如網絡爬蟲,機器人等;與 Subject 的全部交互都會委託給 SecurityManager;Subject 實際上是一個門面,SecurityManager 纔是實際的執行者;
• SecurityManager:安全管理器;即全部與安全有關的操做都會與SecurityManager 交互;且其管理着全部 Subject;能夠看出它是 Shiro的核心,它負責與 Shiro 的其餘組件進行交互,它至關於 SpringMVC 中DispatcherServlet 的角色
• Realm:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說SecurityManager 要驗證用戶身份,那麼它須要從 Realm 獲取相應的用戶進行比較以肯定用戶身份是否合法;也須要從 Realm 獲得用戶相應的角色/權限進行驗證用戶是否能進行操做;能夠把 Realm 當作 DataSource安全


Shiro 架構 (Shiro內部來看)

Shiro 架構
• Subject:任何能夠與應用交互的「用戶」;
• SecurityManager :至關於SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;全部具體的交互都經過 SecurityManager 進行控制;它管理着全部 Subject、且負責進行認證、受權、會話及緩存的管理。
• Authenticator:負責 Subject 認證,是一個擴展點,能夠自定義實現;可使用認證策略(Authentication Strategy),即什麼狀況下算用戶認證經過了;
• Authorizer:受權器、即訪問控制器,用來決定主體是否有權限進行相應的操做;即控制着用戶能訪問應用中的哪些功能;
• Realm:能夠有 1 個或多個 Realm,能夠認爲是安全實體數據源,即用於獲取安全實體的;能夠是JDBC 實現,也能夠是內存實現等等;由用戶提供;因此通常在應用中都須要實現本身的 Realm;
• SessionManager:管理 Session 生命週期的組件;而 Shiro 並不只僅能夠用在 Web環境,也能夠用在如普通的 JavaSE 環境
• CacheManager:緩存控制器,來管理如用戶、角色、權限等的緩存的;由於這些數據基本上不多改變,放到緩存中後能夠提升訪問的性能
• Cryptography:密碼模塊,Shiro 提升了一些常見的加密組件用於如密碼加密/解密。


搭建開發環境

• 加入以下 jar 包:
– shiro-all-1.3.2.jar
– log4j-1.2.15.jar
– slf4j-api-1.6.1.jar
– slf4j-log4j12-1.6.1.jar
• 加入 Spring 和 Shiro 的 jar 包
• 配置 Spring 及 SpringMVC
 

與Web 集成

• Shiro 提供了與 Web 集成的支持,其經過一個ShiroFilter 入口來攔截須要安全控制的URL,而後進行相應的控制
• ShiroFilter 相似於如 Strut2/SpringMVC 這種web 框架的前端控制器,是安全控制的入口點,其負責讀取配置(如ini 配置文件),而後判斷URL是否須要登陸/權限等工做。


ShiroFilter 的工做原理


ShiroFilter
DelegatingFilterProxy 做用是自動到 Spring 容器查找名字爲 shiroFilter(filter-name)的 bean 並把全部 Filter的操做委託給它。

 

部分細節
• [urls] 部分的配置,其格式是: 「url=攔截器[參數],攔截器[參數]」;
• 若是當前請求的 url 匹配 [urls] 部分的某個url 模式,將會執行其配置的攔截器。
• anon(anonymous) 攔截器表示匿名訪問(即不須要登陸便可訪問)
• authc (authentication)攔截器表示須要身份認證經過後才能訪問shiro中默認的過濾器

shiro中默認的過濾器


 

 

URL 匹配模式
• url 模式使用 Ant 風格模式
• Ant 路徑通配符支持 ?、*、**,注意通配符匹配不包括目錄分隔符「/」:
– ?:匹配一個字符,如 /admin? 將匹配 /admin1,但不匹配 /admin 或 /admin/;
– *:匹配零個或多個字符串,如 /admin 將匹配 /admin、admin123,但不匹配 /admin/1;
– **:匹配路徑中的零個或多個路徑,如 /admin/** 將匹配 /admin/a 或 /admin/a/b

URL 匹配順序
•  URL  權限採起第一次匹配優先的 方式,即從頭開始使用第一個匹配的 url 模式對應的攔截器鏈。
• 如:
– /bb/**=filter1
– /bb/aa=filter2
– /**=filter3
– 若是請求的url是「/bb/aa」,由於按照聲明順序進行匹配,那麼將使用 filter1 進行攔截。
 


Shiro 架構 (Shiro外部來看)

• 從外部來看Shiro ,即從應用程序角度的來觀察如何使用 Shiro 完成工做:身份驗證
• 身份驗證:通常須要提供如身份 ID 等一些標識信息來代表登陸者的身份,如提供 email,用戶名/密碼來證實。
• 在 shiro 中,用戶須要提供 principals (身份)和 credentials(證實)給 shiro,從而應用能驗證用戶身份:
• principals:身份,即主體的標識屬性,能夠是任何屬性,如用戶名、郵箱等,惟一便可。一個主體能夠有多個 principals,但只有一個Primary principals,通常是用戶名/郵箱/手機號。
• credentials:證實/憑證,即只有主體知道的安全值,如密碼/數字證書等。
• 最多見的 principals 和 credentials 組合就是用戶名/密碼了

身份驗證基本流程

• 一、收集用戶身份/憑證,即如用戶名/密碼
• 二、調用 Subject.login 進行登陸,若是失敗將獲得相應的 AuthenticationException 異常,根據異常提示用戶錯誤信息;不然登陸成功
• 三、建立自定義的 Realm 類,繼承org.apache.shiro.realm.AuthorizingRealm 類,實現doGetAuthenticationInfo() 方法

 

身份驗證示例

AuthenticationException

• 若是身份驗證失敗請捕獲 AuthenticationException 或其子類
• 最好使用如「用戶名/密碼錯誤」而不是「用戶名錯誤」/「密碼錯誤」,防止一些惡意用戶非法掃描賬號庫;

 


認證流程

 

身份認證流程

• 一、首先調用 Subject.login(token) 進行登陸,其會自動委託給SecurityManager
• 二、SecurityManager 負責真正的身份驗證邏輯;它會委託給Authenticator 進行身份驗證;
• 三、Authenticator 纔是真正的身份驗證者,Shiro API 中核心的身份認證入口點,此處能夠自定義插入本身的實現;
• 四、Authenticator 可能會委託給相應的 AuthenticationStrategy 進行多 Realm 身份驗證,默認 ModularRealmAuthenticator 會調用
AuthenticationStrategy 進行多 Realm 身份驗證;
• 五、Authenticator 會把相應的 token 傳入 Realm,從 Realm 獲取身份驗證信息,若是沒有返回/拋出異常表示身份驗證失敗了。此處能夠配置多個Realm,將按照相應的順序及策略進行訪問。


Realm

• Realm:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),即 SecurityManager 要驗證用戶身份,那麼它須要從 Realm 獲取相應的用戶進行比較以肯定用戶身份是否合法;也須要從Realm獲得用戶相應的角色/權限進行驗證用戶是否能進行操做
• Realm接口以下:


Realm

• 通常繼承 AuthorizingRealm(受權)便可;其繼承了AuthenticatingRealm(即身份驗證),並且也間接繼承了CachingRealm(帶有緩存實現)。
• Realm 的繼承關係:


Authenticator

• Authenticator 的職責是驗證用戶賬號,是 Shiro API 中身份驗證核心的入口點:若是驗證成功,將返回AuthenticationInfo 驗
證信息;此信息中包含了身份及憑證;若是驗證失敗將拋出相應的 AuthenticationException 異常
• SecurityManager 接口繼承了 Authenticator,另外還有一個ModularRealmAuthenticator實現,其委託給多個Realm 進行
驗證,驗證規則經過 AuthenticationStrategy 接口指定


AuthenticationStrategy

• AuthenticationStrategy 接口的默認實現:
• FirstSuccessfulStrategy:只要有一個 Realm 驗證成功便可,只返回第一個 Realm 身份驗證成功的認證信息,其餘的忽略;
• AtLeastOneSuccessfulStrategy:只要有一個Realm驗證成功便可,和FirstSuccessfulStrategy 不一樣,將返回全部Realm身份驗證成功的認證信息;
• AllSuccessfulStrategy:全部Realm驗證成功纔算成功,且返回全部Realm身份驗證成功的認證信息,若是有一個失敗就失敗了。
• ModularRealmAuthenticator 默認是 AtLeastOneSuccessfulStrategy


受權

• 受權,也叫訪問控制,即在應用中控制誰訪問哪些資源(如訪問頁面/編輯數據/頁面操做等)。在受權中需瞭解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限(Permission)、角色(Role)。
• 主體(Subject):訪問應用的用戶,在 Shiro 中使用 Subject 表明該用戶。用戶只有受權後才容許訪問相應的資源。
• 資源(Resource):在應用中用戶能夠訪問的 URL,好比訪問 JSP 頁面、查看/編輯某些數據、訪問某個業務方法、打印文本等等都是資源。用戶只要受權後才能訪問。
• 權限(Permission):安全策略中的原子受權單位,經過權限咱們能夠表示在應用中用戶有沒有操做某個資源的權力。即權限表示在應用中用戶能不能訪問某個資源,如:訪問用戶列表頁面查看/新增/修改/刪除用戶數據(即不少時候都是CRUD(增查改刪)式權限控制)等。權限表明了用戶有沒有操做某個資源的權利,即反映在某個資源上的操做允不容許。
• Shiro 支持粗粒度權限(如用戶模塊的全部權限)和細粒度權限(操做某個用戶的權限,即實例級別的)
• 角色(Role):權限的集合,通常狀況下會賦予用戶角色而不是權限,即這樣用戶能夠擁有一組權限,賦予權限時比較方便。典型的如:項目經理、技術總監、CTO、開發工程師等都是角色,不一樣的角色擁有一組不一樣的權限。


受權方式

• Shiro 支持三種方式的受權:
– 編程式:經過寫if/else 受權代碼塊完成
– 註解式:經過在執行的Java方法上放置相應的註解完成,沒有權限將拋出相應的異常
– JSP/GSP 標籤:在JSP/GSP 頁面經過相應的標籤完成


默認攔截器

• Shiro 內置了不少默認的攔截器,好比身份驗證、受權等相關的。默認攔截器能夠參考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚舉
攔截器:


身份驗證相關的

受權相關的

其餘

Permissions

• 規則: 資源 標識符:操做:對象實例 例  ID 即對哪一個資源的哪一個實例能夠進行什麼操做. 其 默認支持通配符權限字符串,:  表
示 資源/ / 操做/ / 實例的分割;,  表示 操做的 分割,*  表示 任意資源/ / 操做/ / 實例。
•  多層次管理:
– 例如:user:query、user:edit
–  冒號 是一個特殊字符,它用來分隔權限字符串的下一 部件:第一部分是權限被操做的領域(打印機),第二部分是被執行的操做。
– 多個值: 每一個 部件可以保護多個值。所以,除了授予用戶 user:query和 user:edit 權限外,也 能夠 簡單地授予他們一 個: user: query , edit
– 還能夠用 用 *  號 代替全部的值,如:user:* , 也能夠寫:*:query,表示某個用戶在全部的領域都有 query 的權限


Shiro 的 Permissions

•  實例 級訪問控制
– 這種狀況一般會使用三個部件: 域、操做、被付諸實施的實例。如:user:edit:manager
– 也 可使用通配符來定義,如:user:edit:*、user:*:*、user:*:manager
–  部分 省略 通配符:缺乏的部件意味着用戶能夠訪問全部與之匹配的值,好比:user:edit 等價於 user:edit :*、user 等價於 user:*:*
– 注意: 通配符只能 從字符串的結尾處省略部件,也就是說 user:edit 並不等價於 user:*:edit


受權流程


• 流程以下:
• 一、首先調用 Subject.isPermitted*/hasRole* 接口,其會委託給SecurityManager,而 SecurityManager 接着會委託給 Authorizer;
• 二、Authorizer是真正的受權者,若是調用如isPermitted(「user:view」),其首先會經過
• PermissionResolver 把字符串轉換成相應的 Permission 實例;
• 三、在進行受權以前,其會調用相應的 Realm 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;
• 四、Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,若是有多個Realm,會委託給 ModularRealmAuthorizer 進行循環判斷,若是匹配如 isPermitted*/hasRole* 會返回true,不然返回false表示受權失敗。


ModularRealmAuthorizer

• ModularRealmAuthorizer 進行多 Realm 匹配流程:
– 一、首先檢查相應的 Realm 是否實現了實現了Authorizer;
– 二、若是實現了 Authorizer,那麼接着調用其相應的isPermitted*/hasRole* 接口進行匹配;
– 三、若是有一個Realm匹配那麼將返回 true,不然返回 false。


Shiro 標籤

• Shiro 提供了 JSTL 標籤用於在 JSP 頁面進行權限控制,如根據登陸用戶顯示相應的頁面按鈕。
• guest 標籤:用戶沒有身份驗證時顯示相應信息,即遊客訪問信息:


• user 標籤:用戶已經通過認證/記住我登陸後顯示相應的信息。


Shiro 標籤


• authenticated 標籤:用戶已經身份驗證經過,即Subject.login登陸成功,不是記住我登陸的


• notAuthenticated 標籤:用戶未進行身份驗證,即沒有調用Subject.login進行登陸,包括記住我自動登陸的也屬於
未進行身份驗證。


• pincipal 標籤:顯示用戶身份信息,默認調用Subject.getPrincipal() 獲取,即 Primary Principal。


• hasRole 標籤:若是當前 Subject 有角色將顯示 body 體內容:


• hasAnyRoles 標籤:若是當前Subject有任意一個角色(或的關係)將顯示body體內容。


• lacksRole:若是當前 Subject 沒有角色將顯示 body 體內容


• hasPermission:若是當前 Subject 有權限將顯示 body 體內容


• lacksPermission:若是當前Subject沒有權限將顯示body體內容。

 


權限註解

• @RequiresAuthentication:表示當前Subject已經經過login進行了身份驗證;即 Subject. isAuthenticated() 返回 true
• @RequiresUser:表示當前 Subject 已經身份驗證或者經過記住我登陸的。
• @RequiresGuest:表示當前Subject沒有身份驗證或經過記住我登陸過,便是遊客身份。
• @RequiresRoles(value={「admin」, 「user」}, logical=Logical.AND):表示當前 Subject 須要角色 admin 和user
• @RequiresPermissions (value={「user:a」, 「user:b」},logical= Logical.OR):表示當前 Subject 須要權限 user:a 或user:b。


自定義攔截器

• 經過自定義攔截器能夠擴展功能,例如:動態url-角色/權限訪問控制的實現、根據 Subject 身份信息獲取用戶信息綁定到 Request(即設置通用數據)、驗證碼驗證、在線用戶信息的保存等


會話管理

概述
• Shiro 提供了完整的企業級會話管理功能,不依賴於底層容器(如web容器tomcat),無論 JavaSE 仍是 JavaEE 環境
均可以使用,提供了會話管理、會話事件監聽、會話存儲/持久化、容器無關的集羣、失效/過時支持、對Web 的透明支持、SSO 單點登陸的支持等特性。


會話相關的 API

• Subject.getSession():便可獲取會話;其等價於Subject.getSession(true),即若是當前沒有建立 Session 對象會建立
一個;Subject.getSession(false),若是當前沒有建立 Session 則返回null
• session.getId():獲取當前會話的惟一標識
• session.getHost():獲取當前Subject的主機地址
• session.getTimeout() & session.setTimeout(毫秒):獲取/設置當前Session的過時時間
• session.getStartTimestamp() & session.getLastAccessTime():獲取會話的啓動時間及最後訪問時間;若是是 JavaSE 應用須要本身按期調用 session.touch() 去更新最後訪問時間;若是是Web 應用,每次進入 ShiroFilter 都會自動調用 session.touch() 來更新最後訪問時間。
• session.touch() & session.stop():更新會話最後訪問時間及銷燬會話;當Subject.logout()時會自動調用 stop 方法來銷燬會話。若是在web中,調用 HttpSession. invalidate()也會自動調用Shiro Session.stop 方法進行銷燬Shiro 的會話
• session.setAttribute(key, val) &session.getAttribute(key) &session.removeAttribute(key):設置/獲取/刪除會話屬性;在整個會話範圍內均可以對這些屬性進行操做


會話監聽器

• 會話監聽器用於監聽會話建立、過時及中止事件

SessionDao


• AbstractSessionDAO 提供了 SessionDAO 的基礎實現,如生成會話ID等
• CachingSessionDAO 提供了對開發者透明的會話緩存的功能,須要設置相應的 CacheManager
• MemorySessionDAO 直接在內存中進行會話維護
• EnterpriseCacheSessionDAO 提供了緩存功能的會話維護,默認狀況下使用 MapCache 實現,內部使用ConcurrentHashMap 保存緩存的會話。
配置示例


配置示例


數據表
• create table sessions ( id varchar(200),session varchar(2000),constraint pk_sessions primary key(id)) charset=utf8 ENGINE=InnoDB;


Session Dao:


Session Dao:


SerializableUtils:

 


會話驗證

• Shiro 提供了會話驗證調度器,用於按期的驗證會話是否已過時,若是過時將中止會話
• 出於性能考慮,通常狀況下都是獲取會話時來驗證會話是否過時並中止會話的;可是如在 web 環境中,若是用戶不主動退出是不知道會話是否過時的,所以須要按期的檢測會話是否過時,Shiro 提供了會話驗證調度器SessionValidationScheduler
• Shiro 也提供了使用Quartz會話驗證調度器:QuartzSessionValidationScheduler


緩存

CacheManagerAware 接口

• Shiro 內部相應的組件(DefaultSecurityManager)會自動檢測相應的對象(如Realm)是否實現了
CacheManagerAware 並自動注入相應的CacheManager。


Realm 緩存

• Shiro 提供了 CachingRealm,其實現了CacheManagerAware 接口,提供了緩存的一些基礎實現;
• AuthenticatingRealm 及 AuthorizingRealm 也分別提供了對AuthenticationInfo 和 AuthorizationInfo 信息的緩存。


Session 緩存

• 如 SecurityManager 實現了 SessionSecurityManager,其會判斷 SessionManager 是否實現了CacheManagerAware 接口,若是實現了會把CacheManager 設置給它。
• SessionManager 也會判斷相應的 SessionDAO(如繼承自CachingSessionDAO)是否實現了CacheManagerAware,若是實現了會把 CacheManager設置給它
• 設置了緩存的 SessionManager,查詢時會先查緩存,若是找不到才查數據庫。


RememberMe

概述
• Shiro 提供了記住我(RememberMe)的功能,好比訪問如淘寶等一些網站時,關閉了瀏覽器,下次再打開時仍是能記住你是誰,下次訪問時無需再登陸便可訪問,基本流程以下:
• 一、首先在登陸頁面選中 RememberMe 而後登陸成功;若是是瀏覽器登陸,通常會把 RememberMe 的Cookie 寫到客戶端並保存下來;
• 二、關閉瀏覽器再從新打開;會發現瀏覽器仍是記住你的;
• 三、訪問通常的網頁服務器端仍是知道你是誰,且能正常訪問;
• 四、可是好比咱們訪問淘寶時,若是要查看個人訂單或進行支付時,此時仍是須要再進行身份認證的,以確保當前用戶仍是你。認證和記住我
• subject.isAuthenticated() 表示用戶進行了身份驗證登陸的,即便有 Subject.login 進行了登陸;
• subject.isRemembered():表示用戶是經過記住我登陸的,此時可能並非真正的你(如你的朋友使用你的電腦,或者你的cookie 被竊取)在訪問的
• 二者二選一,即 subject.isAuthenticated()==true,則subject.isRemembered()==false;反之同樣。


建議

• 訪問通常網頁:如我的在主頁之類的,咱們使用user 攔截器便可,user 攔截器只要用戶登陸(isRemembered() || isAuthenticated())過便可訪問成功;
• 訪問特殊網頁:如個人訂單,提交訂單頁面,咱們使用authc 攔截器便可,authc 攔截器會判斷用戶是不是經過Subject.login(isAuthenticated()==true)登陸的,若是是才放行,不然會跳轉到登陸頁面叫你從新登陸。


身份驗證相關的

實現

• 若是要本身作RememeberMe,須要在登陸以前這樣建立Token:UsernamePasswordToken(用戶名,密碼,是否記住我),且調用UsernamePasswordToken 的:token.setRememberMe(true); 方法

---恢復內容結束---

Shiro 簡介

簡介
• Apache Shiro 是 Java 的一個安全(權限)框架。
• Shiro 能夠很是容易的開發出足夠好的應用,其不只能夠用在JavaSE 環境,也能夠用在 JavaEE 環境。
• Shiro 能夠完成:認證、受權、加密、會話管理、與Web 集成、緩存等。
• 下載:http://shiro.apache.org/


功能簡介

• 基本功能點以下圖所示:


功能簡介
• Authentication:身份認證/登陸,驗證用戶是否是擁有相應的身份;
• Authorization:受權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能進行什麼操做,如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具備某個權限;
• Session Manager:會話管理,即用戶登陸後就是一次會話,在沒有退出以前,它的全部信息都在會話中;會話能夠是普通 JavaSE 環境,也能夠是 Web 環境的;
• Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
• Web Support:Web 支持,能夠很是容易的集成到Web 環境;
• Caching:緩存,好比用戶登陸後,其用戶信息、擁有的角色/權限沒必要每次去查,這樣能夠提升效率;
• Concurrency:Shiro 支持多線程應用的併發驗證,即如在一個線程中開啓另外一個線程,能
• 把權限自動傳播過去;
• Testing:提供測試支持;
• Run As:容許一個用戶僞裝爲另外一個用戶(若是他們容許)的身份進行訪問;
• Remember Me:記住我,這個是很是常見的功能,即一次登陸後,下次再來的話不用登陸了


Shiro 架構 (Shiro外部來看)

• 從外部來看Shiro ,即從應用程序角度的來觀察如何使用 Shiro 完成工做:


Shiro 架構

• Subject:應用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外API 核心就是 Subject。Subject 表明了當前「用戶」, 這個用戶不必定是一個具體的人,與當前應用交互的任何東西都是 Subject,如網絡爬蟲,機器人等;與 Subject 的全部交互都會委託給 SecurityManager;Subject 實際上是一個門面,SecurityManager 纔是實際的執行者;
• SecurityManager:安全管理器;即全部與安全有關的操做都會與SecurityManager 交互;且其管理着全部 Subject;能夠看出它是 Shiro的核心,它負責與 Shiro 的其餘組件進行交互,它至關於 SpringMVC 中DispatcherServlet 的角色
• Realm:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說SecurityManager 要驗證用戶身份,那麼它須要從 Realm 獲取相應的用戶進行比較以肯定用戶身份是否合法;也須要從 Realm 獲得用戶相應的角色/權限進行驗證用戶是否能進行操做;能夠把 Realm 當作 DataSource


Shiro 架構 (Shiro內部來看)

Shiro 架構
• Subject:任何能夠與應用交互的「用戶」;
• SecurityManager :至關於SpringMVC 中的 DispatcherServlet;是 Shiro 的心臟;全部具體的交互都經過 SecurityManager 進行控制;它管理着全部 Subject、且負責進行認證、受權、會話及緩存的管理。
• Authenticator:負責 Subject 認證,是一個擴展點,能夠自定義實現;可使用認證策略(Authentication Strategy),即什麼狀況下算用戶認證經過了;
• Authorizer:受權器、即訪問控制器,用來決定主體是否有權限進行相應的操做;即控制着用戶能訪問應用中的哪些功能;
• Realm:能夠有 1 個或多個 Realm,能夠認爲是安全實體數據源,即用於獲取安全實體的;能夠是JDBC 實現,也能夠是內存實現等等;由用戶提供;因此通常在應用中都須要實現本身的 Realm;
• SessionManager:管理 Session 生命週期的組件;而 Shiro 並不只僅能夠用在 Web環境,也能夠用在如普通的 JavaSE 環境
• CacheManager:緩存控制器,來管理如用戶、角色、權限等的緩存的;由於這些數據基本上不多改變,放到緩存中後能夠提升訪問的性能
• Cryptography:密碼模塊,Shiro 提升了一些常見的加密組件用於如密碼加密/解密。


搭建開發環境

• 加入以下 jar 包:
– shiro-all-1.3.2.jar
– log4j-1.2.15.jar
– slf4j-api-1.6.1.jar
– slf4j-log4j12-1.6.1.jar
• 加入 Spring 和 Shiro 的 jar 包
• 配置 Spring 及 SpringMVC
 

與Web 集成

• Shiro 提供了與 Web 集成的支持,其經過一個ShiroFilter 入口來攔截須要安全控制的URL,而後進行相應的控制
• ShiroFilter 相似於如 Strut2/SpringMVC 這種web 框架的前端控制器,是安全控制的入口點,其負責讀取配置(如ini 配置文件),而後判斷URL是否須要登陸/權限等工做。


ShiroFilter 的工做原理


ShiroFilter
DelegatingFilterProxy 做用是自動到 Spring 容器查找名字爲 shiroFilter(filter-name)的 bean 並把全部 Filter的操做委託給它。

 

部分細節
• [urls] 部分的配置,其格式是: 「url=攔截器[參數],攔截器[參數]」;
• 若是當前請求的 url 匹配 [urls] 部分的某個url 模式,將會執行其配置的攔截器。
• anon(anonymous) 攔截器表示匿名訪問(即不須要登陸便可訪問)
• authc (authentication)攔截器表示須要身份認證經過後才能訪問shiro中默認的過濾器

shiro中默認的過濾器


 

 

URL 匹配模式
• url 模式使用 Ant 風格模式
• Ant 路徑通配符支持 ?、*、**,注意通配符匹配不包括目錄分隔符「/」:
– ?:匹配一個字符,如 /admin? 將匹配 /admin1,但不匹配 /admin 或 /admin/;
– *:匹配零個或多個字符串,如 /admin 將匹配 /admin、admin123,但不匹配 /admin/1;
– **:匹配路徑中的零個或多個路徑,如 /admin/** 將匹配 /admin/a 或 /admin/a/b

URL 匹配順序
•  URL  權限採起第一次匹配優先的 方式,即從頭開始使用第一個匹配的 url 模式對應的攔截器鏈。
• 如:
– /bb/**=filter1
– /bb/aa=filter2
– /**=filter3
– 若是請求的url是「/bb/aa」,由於按照聲明順序進行匹配,那麼將使用 filter1 進行攔截。
 


Shiro 架構 (Shiro外部來看)

• 從外部來看Shiro ,即從應用程序角度的來觀察如何使用 Shiro 完成工做:身份驗證
• 身份驗證:通常須要提供如身份 ID 等一些標識信息來代表登陸者的身份,如提供 email,用戶名/密碼來證實。
• 在 shiro 中,用戶須要提供 principals (身份)和 credentials(證實)給 shiro,從而應用能驗證用戶身份:
• principals:身份,即主體的標識屬性,能夠是任何屬性,如用戶名、郵箱等,惟一便可。一個主體能夠有多個 principals,但只有一個Primary principals,通常是用戶名/郵箱/手機號。
• credentials:證實/憑證,即只有主體知道的安全值,如密碼/數字證書等。
• 最多見的 principals 和 credentials 組合就是用戶名/密碼了

身份驗證基本流程

• 一、收集用戶身份/憑證,即如用戶名/密碼
• 二、調用 Subject.login 進行登陸,若是失敗將獲得相應的 AuthenticationException 異常,根據異常提示用戶錯誤信息;不然登陸成功
• 三、建立自定義的 Realm 類,繼承org.apache.shiro.realm.AuthorizingRealm 類,實現doGetAuthenticationInfo() 方法

 

身份驗證示例

AuthenticationException

• 若是身份驗證失敗請捕獲 AuthenticationException 或其子類
• 最好使用如「用戶名/密碼錯誤」而不是「用戶名錯誤」/「密碼錯誤」,防止一些惡意用戶非法掃描賬號庫;

 


認證流程

 

身份認證流程

• 一、首先調用 Subject.login(token) 進行登陸,其會自動委託給SecurityManager
• 二、SecurityManager 負責真正的身份驗證邏輯;它會委託給Authenticator 進行身份驗證;
• 三、Authenticator 纔是真正的身份驗證者,Shiro API 中核心的身份認證入口點,此處能夠自定義插入本身的實現;
• 四、Authenticator 可能會委託給相應的 AuthenticationStrategy 進行多 Realm 身份驗證,默認 ModularRealmAuthenticator 會調用
AuthenticationStrategy 進行多 Realm 身份驗證;
• 五、Authenticator 會把相應的 token 傳入 Realm,從 Realm 獲取身份驗證信息,若是沒有返回/拋出異常表示身份驗證失敗了。此處能夠配置多個Realm,將按照相應的順序及策略進行訪問。


Realm

• Realm:Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),即 SecurityManager 要驗證用戶身份,那麼它須要從 Realm 獲取相應的用戶進行比較以肯定用戶身份是否合法;也須要從Realm獲得用戶相應的角色/權限進行驗證用戶是否能進行操做
• Realm接口以下:


Realm

• 通常繼承 AuthorizingRealm(受權)便可;其繼承了AuthenticatingRealm(即身份驗證),並且也間接繼承了CachingRealm(帶有緩存實現)。
• Realm 的繼承關係:


Authenticator

• Authenticator 的職責是驗證用戶賬號,是 Shiro API 中身份驗證核心的入口點:若是驗證成功,將返回AuthenticationInfo 驗
證信息;此信息中包含了身份及憑證;若是驗證失敗將拋出相應的 AuthenticationException 異常
• SecurityManager 接口繼承了 Authenticator,另外還有一個ModularRealmAuthenticator實現,其委託給多個Realm 進行
驗證,驗證規則經過 AuthenticationStrategy 接口指定


AuthenticationStrategy

• AuthenticationStrategy 接口的默認實現:
• FirstSuccessfulStrategy:只要有一個 Realm 驗證成功便可,只返回第一個 Realm 身份驗證成功的認證信息,其餘的忽略;
• AtLeastOneSuccessfulStrategy:只要有一個Realm驗證成功便可,和FirstSuccessfulStrategy 不一樣,將返回全部Realm身份驗證成功的認證信息;
• AllSuccessfulStrategy:全部Realm驗證成功纔算成功,且返回全部Realm身份驗證成功的認證信息,若是有一個失敗就失敗了。
• ModularRealmAuthenticator 默認是 AtLeastOneSuccessfulStrategy


受權

• 受權,也叫訪問控制,即在應用中控制誰訪問哪些資源(如訪問頁面/編輯數據/頁面操做等)。在受權中需瞭解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限(Permission)、角色(Role)。
• 主體(Subject):訪問應用的用戶,在 Shiro 中使用 Subject 表明該用戶。用戶只有受權後才容許訪問相應的資源。
• 資源(Resource):在應用中用戶能夠訪問的 URL,好比訪問 JSP 頁面、查看/編輯某些數據、訪問某個業務方法、打印文本等等都是資源。用戶只要受權後才能訪問。
• 權限(Permission):安全策略中的原子受權單位,經過權限咱們能夠表示在應用中用戶有沒有操做某個資源的權力。即權限表示在應用中用戶能不能訪問某個資源,如:訪問用戶列表頁面查看/新增/修改/刪除用戶數據(即不少時候都是CRUD(增查改刪)式權限控制)等。權限表明了用戶有沒有操做某個資源的權利,即反映在某個資源上的操做允不容許。
• Shiro 支持粗粒度權限(如用戶模塊的全部權限)和細粒度權限(操做某個用戶的權限,即實例級別的)
• 角色(Role):權限的集合,通常狀況下會賦予用戶角色而不是權限,即這樣用戶能夠擁有一組權限,賦予權限時比較方便。典型的如:項目經理、技術總監、CTO、開發工程師等都是角色,不一樣的角色擁有一組不一樣的權限。


受權方式

• Shiro 支持三種方式的受權:
– 編程式:經過寫if/else 受權代碼塊完成
– 註解式:經過在執行的Java方法上放置相應的註解完成,沒有權限將拋出相應的異常
– JSP/GSP 標籤:在JSP/GSP 頁面經過相應的標籤完成


默認攔截器

• Shiro 內置了不少默認的攔截器,好比身份驗證、受權等相關的。默認攔截器能夠參考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚舉
攔截器:


身份驗證相關的

受權相關的

其餘

Permissions

• 規則: 資源 標識符:操做:對象實例 例  ID 即對哪一個資源的哪一個實例能夠進行什麼操做. 其 默認支持通配符權限字符串,:  表
示 資源/ / 操做/ / 實例的分割;,  表示 操做的 分割,*  表示 任意資源/ / 操做/ / 實例。
•  多層次管理:
– 例如:user:query、user:edit
–  冒號 是一個特殊字符,它用來分隔權限字符串的下一 部件:第一部分是權限被操做的領域(打印機),第二部分是被執行的操做。
– 多個值: 每一個 部件可以保護多個值。所以,除了授予用戶 user:query和 user:edit 權限外,也 能夠 簡單地授予他們一 個: user: query , edit
– 還能夠用 用 *  號 代替全部的值,如:user:* , 也能夠寫:*:query,表示某個用戶在全部的領域都有 query 的權限


Shiro 的 Permissions

•  實例 級訪問控制
– 這種狀況一般會使用三個部件: 域、操做、被付諸實施的實例。如:user:edit:manager
– 也 可使用通配符來定義,如:user:edit:*、user:*:*、user:*:manager
–  部分 省略 通配符:缺乏的部件意味着用戶能夠訪問全部與之匹配的值,好比:user:edit 等價於 user:edit :*、user 等價於 user:*:*
– 注意: 通配符只能 從字符串的結尾處省略部件,也就是說 user:edit 並不等價於 user:*:edit


受權流程


• 流程以下:
• 一、首先調用 Subject.isPermitted*/hasRole* 接口,其會委託給SecurityManager,而 SecurityManager 接着會委託給 Authorizer;
• 二、Authorizer是真正的受權者,若是調用如isPermitted(「user:view」),其首先會經過
• PermissionResolver 把字符串轉換成相應的 Permission 實例;
• 三、在進行受權以前,其會調用相應的 Realm 獲取 Subject 相應的角色/權限用於匹配傳入的角色/權限;
• 四、Authorizer 會判斷 Realm 的角色/權限是否和傳入的匹配,若是有多個Realm,會委託給 ModularRealmAuthorizer 進行循環判斷,若是匹配如 isPermitted*/hasRole* 會返回true,不然返回false表示受權失敗。


ModularRealmAuthorizer

• ModularRealmAuthorizer 進行多 Realm 匹配流程:
– 一、首先檢查相應的 Realm 是否實現了實現了Authorizer;
– 二、若是實現了 Authorizer,那麼接着調用其相應的isPermitted*/hasRole* 接口進行匹配;
– 三、若是有一個Realm匹配那麼將返回 true,不然返回 false。


Shiro 標籤

• Shiro 提供了 JSTL 標籤用於在 JSP 頁面進行權限控制,如根據登陸用戶顯示相應的頁面按鈕。
• guest 標籤:用戶沒有身份驗證時顯示相應信息,即遊客訪問信息:


• user 標籤:用戶已經通過認證/記住我登陸後顯示相應的信息。


Shiro 標籤


• authenticated 標籤:用戶已經身份驗證經過,即Subject.login登陸成功,不是記住我登陸的


• notAuthenticated 標籤:用戶未進行身份驗證,即沒有調用Subject.login進行登陸,包括記住我自動登陸的也屬於
未進行身份驗證。


• pincipal 標籤:顯示用戶身份信息,默認調用Subject.getPrincipal() 獲取,即 Primary Principal。


• hasRole 標籤:若是當前 Subject 有角色將顯示 body 體內容:


• hasAnyRoles 標籤:若是當前Subject有任意一個角色(或的關係)將顯示body體內容。


• lacksRole:若是當前 Subject 沒有角色將顯示 body 體內容


• hasPermission:若是當前 Subject 有權限將顯示 body 體內容


• lacksPermission:若是當前Subject沒有權限將顯示body體內容。

 


權限註解

• @RequiresAuthentication:表示當前Subject已經經過login進行了身份驗證;即 Subject. isAuthenticated() 返回 true
• @RequiresUser:表示當前 Subject 已經身份驗證或者經過記住我登陸的。
• @RequiresGuest:表示當前Subject沒有身份驗證或經過記住我登陸過,便是遊客身份。
• @RequiresRoles(value={「admin」, 「user」}, logical=Logical.AND):表示當前 Subject 須要角色 admin 和user
• @RequiresPermissions (value={「user:a」, 「user:b」},logical= Logical.OR):表示當前 Subject 須要權限 user:a 或user:b。


自定義攔截器

• 經過自定義攔截器能夠擴展功能,例如:動態url-角色/權限訪問控制的實現、根據 Subject 身份信息獲取用戶信息綁定到 Request(即設置通用數據)、驗證碼驗證、在線用戶信息的保存等


會話管理

概述
• Shiro 提供了完整的企業級會話管理功能,不依賴於底層容器(如web容器tomcat),無論 JavaSE 仍是 JavaEE 環境
均可以使用,提供了會話管理、會話事件監聽、會話存儲/持久化、容器無關的集羣、失效/過時支持、對Web 的透明支持、SSO 單點登陸的支持等特性。


會話相關的 API

• Subject.getSession():便可獲取會話;其等價於Subject.getSession(true),即若是當前沒有建立 Session 對象會建立
一個;Subject.getSession(false),若是當前沒有建立 Session 則返回null
• session.getId():獲取當前會話的惟一標識
• session.getHost():獲取當前Subject的主機地址
• session.getTimeout() & session.setTimeout(毫秒):獲取/設置當前Session的過時時間
• session.getStartTimestamp() & session.getLastAccessTime():獲取會話的啓動時間及最後訪問時間;若是是 JavaSE 應用須要本身按期調用 session.touch() 去更新最後訪問時間;若是是Web 應用,每次進入 ShiroFilter 都會自動調用 session.touch() 來更新最後訪問時間。
• session.touch() & session.stop():更新會話最後訪問時間及銷燬會話;當Subject.logout()時會自動調用 stop 方法來銷燬會話。若是在web中,調用 HttpSession. invalidate()也會自動調用Shiro Session.stop 方法進行銷燬Shiro 的會話
• session.setAttribute(key, val) &session.getAttribute(key) &session.removeAttribute(key):設置/獲取/刪除會話屬性;在整個會話範圍內均可以對這些屬性進行操做


會話監聽器

• 會話監聽器用於監聽會話建立、過時及中止事件

SessionDao


• AbstractSessionDAO 提供了 SessionDAO 的基礎實現,如生成會話ID等
• CachingSessionDAO 提供了對開發者透明的會話緩存的功能,須要設置相應的 CacheManager
• MemorySessionDAO 直接在內存中進行會話維護
• EnterpriseCacheSessionDAO 提供了緩存功能的會話維護,默認狀況下使用 MapCache 實現,內部使用ConcurrentHashMap 保存緩存的會話。
配置示例


配置示例


數據表
• create table sessions ( id varchar(200),session varchar(2000),constraint pk_sessions primary key(id)) charset=utf8 ENGINE=InnoDB;


Session Dao:


Session Dao:


SerializableUtils:

 


會話驗證

• Shiro 提供了會話驗證調度器,用於按期的驗證會話是否已過時,若是過時將中止會話
• 出於性能考慮,通常狀況下都是獲取會話時來驗證會話是否過時並中止會話的;可是如在 web 環境中,若是用戶不主動退出是不知道會話是否過時的,所以須要按期的檢測會話是否過時,Shiro 提供了會話驗證調度器SessionValidationScheduler
• Shiro 也提供了使用Quartz會話驗證調度器:QuartzSessionValidationScheduler


緩存

CacheManagerAware 接口

• Shiro 內部相應的組件(DefaultSecurityManager)會自動檢測相應的對象(如Realm)是否實現了
CacheManagerAware 並自動注入相應的CacheManager。


Realm 緩存

• Shiro 提供了 CachingRealm,其實現了CacheManagerAware 接口,提供了緩存的一些基礎實現;
• AuthenticatingRealm 及 AuthorizingRealm 也分別提供了對AuthenticationInfo 和 AuthorizationInfo 信息的緩存。


Session 緩存

• 如 SecurityManager 實現了 SessionSecurityManager,其會判斷 SessionManager 是否實現了CacheManagerAware 接口,若是實現了會把CacheManager 設置給它。
• SessionManager 也會判斷相應的 SessionDAO(如繼承自CachingSessionDAO)是否實現了CacheManagerAware,若是實現了會把 CacheManager設置給它
• 設置了緩存的 SessionManager,查詢時會先查緩存,若是找不到才查數據庫。


RememberMe

概述
• Shiro 提供了記住我(RememberMe)的功能,好比訪問如淘寶等一些網站時,關閉了瀏覽器,下次再打開時仍是能記住你是誰,下次訪問時無需再登陸便可訪問,基本流程以下:
• 一、首先在登陸頁面選中 RememberMe 而後登陸成功;若是是瀏覽器登陸,通常會把 RememberMe 的Cookie 寫到客戶端並保存下來;
• 二、關閉瀏覽器再從新打開;會發現瀏覽器仍是記住你的;
• 三、訪問通常的網頁服務器端仍是知道你是誰,且能正常訪問;
• 四、可是好比咱們訪問淘寶時,若是要查看個人訂單或進行支付時,此時仍是須要再進行身份認證的,以確保當前用戶仍是你。認證和記住我
• subject.isAuthenticated() 表示用戶進行了身份驗證登陸的,即便有 Subject.login 進行了登陸;
• subject.isRemembered():表示用戶是經過記住我登陸的,此時可能並非真正的你(如你的朋友使用你的電腦,或者你的cookie 被竊取)在訪問的
• 二者二選一,即 subject.isAuthenticated()==true,則subject.isRemembered()==false;反之同樣。


建議

• 訪問通常網頁:如我的在主頁之類的,咱們使用user 攔截器便可,user 攔截器只要用戶登陸(isRemembered() || isAuthenticated())過便可訪問成功;
• 訪問特殊網頁:如個人訂單,提交訂單頁面,咱們使用authc 攔截器便可,authc 攔截器會判斷用戶是不是經過Subject.login(isAuthenticated()==true)登陸的,若是是才放行,不然會跳轉到登陸頁面叫你從新登陸。


身份驗證相關的

實現

• 若是要本身作RememeberMe,須要在登陸以前這樣建立Token:UsernamePasswordToken(用戶名,密碼,是否記住我),且調用UsernamePasswordToken 的:token.setRememberMe(true); 方法

相關文章
相關標籤/搜索