apache-shiro入門<一>


  Apache Shiro是一個強大而靈活的開源安全框架(本來想傳到網盤供大家下載,但是鑒於國內網盤動不動就要關閉清楚用戶數據;所以我提供了另一個shiro的中文文檔下載鏈接:http://download.csdn.net/detail/privacy_googol/9668807),它干凈利落地處理身份認證,授權,企業會話管理和加密。在給大家介紹Shiro前,先給小白補充一點權限基礎的知識。

      安全實體:就是被權限系統保護的對象,比如工資數據。  

    權限:就是需要被校驗的行為,比如查看、修改等。

     分配權限:把對某些安全實體的某些權限分配給某些人員。是向數據庫里面添加數據、或是維護數據的過程

    權限驗證(權限匹配):判斷某個人員或程序對某個安全實體是否擁有某個或某些權限。從數據庫中獲取相應數據進行匹配的過程。

    權限的繼承性:如果多個安全實體存在包含關系,而某個安全實體沒有權限限制,則它會繼承包含它的安全實體的相應權限。

    權限的最近匹配原則:如果多個安全實體存在包含關系,而某個安全實體沒有權限限制,那么它會向上尋找並匹配相應權限限制,直到找到一個離這個安全實體最近的擁有相應權限限制的安全實體為止。如果把整個層次結構都

了都沒有匹配到相應權限限制的話,那就說明所有人對這個安全實體都擁有這個相應的權限限制。

  Shiro 簡介

  Apache Shiro 是一個強大易用的 Java 安全框架,提供了認證、授權、加密和會話管理等功能 。
    Shiro 能做什么 ???
      認證:驗證用戶的身份
      授權:對用戶執行訪問控制:判斷用戶是否被允許做某事
      管理:在任何環境下使用 Session API,即使沒有 Web 或EJB 容器。
      加密:以更簡潔易用的方式使用加密功能,保護或隱藏數據防止被偷窺
      Realms:聚集一個或多個用戶安全數據的數據源
      單點登錄(SSO)功能:為沒有關聯到登錄的用戶啟用 "Remember Me“ 服務

  Shiro 的主要功能

 

   

 

  Shiro 的四大核心部分

    Authentication(身份驗證):簡稱為“登錄”,即證明用戶是誰。

    Authorization(授權):訪問控制的過程,即決定是否有權限去訪問受保護的資源。

    Session Management(會話管理):管理用戶特定的會話,即使在非 Web 或 EJB 應用程序。

    Cryptography(加密):通過使用加密算法保持數據安全

  shiro 還提供以下擴展:

    Web Support:主要針對web應用提供一些常用功能。

    Caching:緩存可以使應用程序運行更有效率。

    Concurrency:多線程相關功能。

    Testing:幫助我們進行測試相關功能

    "Run As":一個允許用戶假設為另一個用戶身份(如果允許)的功能,有時候在管理腳本很有用。

    "Remember Me" :記住用戶身份,提供類似購物車功能。

Shiro 架構 3 個核心組件

 

 

  Subject :正與系統進行交互的人,或某一個第三方服務。所有 Subject 實例都被綁定到(且這是必須的)一個SecurityManager 上。

  SecurityManager:Shiro 架構的心臟,用來協調內部各安全組件,管理內部組件實例,並通過它來提供安全管理的各種服務。當 Shiro 與一個 Subject 進行交互時,實質上是幕后的 SecurityManager 處理所有繁重的

Subject 安全操作。

  Realms :本質上是一個特定安全的 DAO。當配置 Shiro 時,必須指定至少一個 Realm 用來進行身份驗證或授權。Shiro 提供了多種可用的 Realms 來獲取安全相關的數據。如關系數據庫(JDBC),INI 及屬性文件等。可以

定義自己 Realm 實現來代表自定義的數據源。

Shiro 架構

  

 

 

  Authenticator :執行對用戶的身份驗證(登錄)的組件。Authenticator 從一個或多個 Realm 中獲得數據以驗證用戶的身份。 若存在多個realm,則接口 AuthenticationStrategy 會確定什么樣算是驗證成功(例如,如果一個 Realm 成功,而其他的均失敗,是否登錄成功)。

  Authorizer :驗證用戶能否訪問應用中的受保護的資源。

  SessionManager :可在任何應用或架構層一致地使用 Session API

  SessionDAO:SessionManager 執行 Session 持久化(CRUD)操作。

  CacheManager :對 Shiro 組件提供緩存支持。

  Cryptography:Shiro 的 API 大幅度簡化 Java API 中繁瑣的密碼加密

  Realms:Shiro 通過 Realms 來獲取相應的安全數據

 Shiro 配置基礎:users

  Shiro 被設計成能夠在任何環境下工作,從簡單的命令行應用程序到企業群集應用。由於環境的多樣性,使得 Shiro 可以使用多種配置機制。

    ini 配置:ini 實際上是一個文本配置,包含了由唯一命名的項來組織的鍵/值對。

    [users] 部分允許定義一組靜態的用戶帳戶

      每行的格式:username = password, roleName1, roleName2, …

Shiro 配置基礎:roles

  [roles] 部分允許把定義在 [users] 部分中的角色與權限關聯起來

    每行的格式:rolename = permissionDefinition1, permissionDefinition2, …

    permissionDefinition 是一個任意的字符串,但大多數人將會使用符合 org.apache.shiro.authz.permission.WildcardPermission 格式的字符串。

  注意:

    如果一個獨立的 permissionDefinition 需要被內部逗號分隔(例如,printer:5thFloor:print,info),則需要用戶雙引號環繞該定義,以避免錯誤解析。  

    如果角色不想關聯權限,則不需要在 [roles] 部分把它們列出來。只需定義在 [user] 部分中定義角色名就足以創建尚不存在的角色。

    僅定義非空的 [users] 或 [roles] 部分就將自動地觸發org.apache.shiro.realm.text.IniRealm 實例的創建

Shiro 的 Permissions

  基礎語法之簡單的字符串:即用簡單的字符串來表示一個權限,如:user (相當於:user:*)

  基礎語法之多層次管理:

    例如:user:query、user:edit 

    

    多個值:每個部件能夠保護多個值。因此,除了授予用戶 user:query 和 user:edit 權限外,也可以簡單地授予他們一個:user:query, edit

    還可以用 * 號代替所有的值,如:user:* , 也可以寫:*:query,表示某個用戶在所有的領域都有 query 的權限

 

  基礎語法之實例級訪問控制

    這種情況通常會使用三個部件:域、操作、被付諸實施的實例。如:user:edit:manager

    也可以使用通配符來定義,如:user:edit:*、user:*:*、user:*:manager

    部分省略通配符:缺少的部件意味着用戶可以訪問所有與之匹配的值,比如:user:edit  等價於 user:edit :*、user  等價於 user:*:*

  注意:通配符只能從字符串的結尾處省略部件,也就是說 user:edit  並不等價於 user:*:edit

身份認證:Authentication

  Authentication :身份驗證——通過提交用戶的身份和憑證給 Shiro,以判斷它們是否和應用程序預期的相匹配。

  基本概念

    Principals(身份):Subject 的 identifying attributes(標識屬性)。比如我們登錄提交的用戶名。

    Credentials(憑證):用來作為一種起支持作用的證據,此證據包含身份證明。比如我們登錄提供的密碼

  認證的基本步驟

    收集Subjects 提交的Principals(身份)和Credentials(憑證);

    提交Principals(身份)和Credentials(憑證)進行身份驗證;

    如果提交成功,則允許訪問,否則重新進行身份驗證或者阻止訪問。

  AuthenticationToken:Shiro 中代表提交的 Principals(身份) 和 Credentials (憑證) 的身份驗證系統的最基本接口。

  UsernamePasswordToken :AuthenticationToken 的接口的實現類,支持最常見的用戶名/密碼的身份驗證

  提交用戶名/密碼進行認證

    Subject currentUser = SecurityUtils.getSubject();

    currentUser.login(token);

  處理認證成功和失敗

    認證成功:沒有返回,也沒有異常,通過。

    認證失敗,拋出異常,可以在程序中捕獲並處理

  認證順序

認證過程

 

  Step 1:應用程序代碼調用 Subject.login 方法,傳遞創建好的包含終端用戶的 Principals(身份)和 Credentials(憑證)的 AuthenticationToken 實例

  Step 2:Subject 實例,通常為 DelegatingSubject(或子類)委托應用程序的 SecurityManager 通過調用 securityManager.login(token) 開始真正的驗證。

  Step 3:SubjectManager 接收 token,調用內部的 Authenticator 實例調用 authenticator.authenticate(token)。 Authenticator 通常是一個 ModularRealmAuthenticator 實例,支持在身份驗證中協調一個或多個Realm 實例。

  Step 4:如果應用程序中配置了一個以上的 Realm,ModularRealmAuthenticator 實例將利用配置好的AuthenticationStrategy 來啟動 Multi-Realm 認證嘗試。在Realms 被身份驗證調用之前,期間和以后,

AuthenticationStrategy 被調用使其能夠對每個Realm 的結果作出反應。

  Step 5:每個配置的 Realm 用來幫助看它是否支持提交的AuthenticationToken。如果支持,那么支持 Realm 的 getAuthenticationInfo 方法將會伴隨着提交的 token 被調用。getAuthenticationInfo 方法有效地代表一

特定 Realm 的單一的身份驗證嘗試。

 注銷

  logout(注銷):currentUser.logout();

  調用 logout() 方法時,現有 Session 將失效,而且身份將失去關聯(在Web 應用程序中,RememberMe cookie 將被刪除)。

  在 Subject 注銷后,該 Subject 的實例被再次認為是匿名的。

  注意:WEB 應用程序記住身份往往依靠 Cookie,然而Cookie 只能在 Response 被返回后被刪除,所以建議在調用subject.logout() 后立即向終端重定向一個新的視圖或頁面。這樣即能保證與安全相關的 Cookie 都能像預期的

一樣被刪除。

  授權: Authorization

  授權:又稱訪問控制—控制誰有權限在應用程序中做什么。

  授權檢查的例子:用戶是否能訪問某個網頁,編輯數據,或打使用這台打印機

  授權的三要素:權限、角色和用戶 。

  需要在應用程序中對用戶和權限建立關聯:通常的做法是將權限分配給角色,然后將角色分配給一個或多個用戶。

  權限:Shiro 安全機制最核心的元素。它在應用程序中明確聲明了被允許的行為。一個格式良好的權限聲明可以清晰表達出用戶對該資源擁有的權限。在 Shiro 中主要通過通配符表達式來完成權限的描述。

 角色:Role

  角色:一個命名的實體, 通常代表一組行為或職責。 這些行為演化為在一個應用中能或者不能做的事情。角色通常分配給用戶帳戶

     一個角色擁有一個權限的集合。授權驗證時,需要判斷當前角色是否擁有指定的權限。這種角色權限可以對該角色進行詳細的權限描述。 Shiro官方推薦使用這種方式。

  Shiro的三種授權方式

    編寫代碼:在 Java 代碼中用像 if 和 else 塊的結構執行授權檢查。

    JDK 的注解:可以添加授權注解給 Java 方法

    JSP 標簽庫:可以控制基於角色和權限的JSP 頁面輸出。

編程授權

  通過使用 subject 的方法來實現角色的判斷,常用的 API:

    hasRole(String roleName)

    hasRoles(List<String> roleNames)

    hasAllRoles(Collection<String> roleNames)

  斷言支持:

    Shiro 還支持以斷言的方式進行授權驗證。斷言成功,不返回任何值,程序繼續執行;斷言失敗時,將拋出異常信息。常用方法:

    checkRole(String roleName) 、

    checkRoles(Collection<String>roleNames)、

    checkRoles(String… roleNames)

 

  基於權限對象的實現

    isPermitted(Permission p)、isPermitted(List<Permission> perms)、isPermittedAll(Collection<Permission> perms)  

  基於字符串的實現

    if (currentUser.isPermitted("printer:print:laserjet4400n"))

    isPermitted(String perm)、isPermitted(String... perms)、isPermittedAll(String... perms)

  權限的實現也都可以采用斷言的方式,相關方法:

    checkPermission(Permission p)、checkPermission(String perm)、checkPermissions(Collection<Permission> perms)、checkPermissions(String... perms)

授權的順序

 

  Step 1:應用程序或框架代碼調用任何 Subject 的hasRole*, checkRole*, isPermitted*,或者checkPermission*方法的變體,傳遞任何所需的權限

  Step 2:Subject 的實例—通常是 DelegatingSubject(或子類),調用securityManager 的對應的方法。

  Step 3:SecurityManager 調用 org.apache.shiro.authz.Authorizer 接口的對應方法。默認情況下,authorizer 實例是一個 ModularRealmAuthorizer 實例,它支持協調任何授權操作過程中的一個或多個Realm 實例。

  Step 4:每個配置好的 Realm 被檢查是否實現了相同的 Authorizer 接口。如果是,Realm 各自的 hasRole*, checkRole*,isPermitted*,或 checkPermission* 方法將被調用。

Realm

  Realm:訪問應用程序安全數據(如用戶、角色及權限)的組件。

  Realm 通常和數據源是一對一的對應關系,如關系數據庫、文件系統或其他類似資源。Realm 實質上就是一個訪問安全數據的 DAO。

  數據源通常存儲身份驗證數據(如密碼的憑證)以及授權數據(如角色或權限),所以每個Realm 都能夠執行身份驗證和授權操作。

  Realms的認證實現

  Shiro 的認證過程由 Realm 執行,SecurityManager 會調用 org.apache.shiro.realm.Realm 的 getAuthenticationInfo(AuthenticationToken token) 方法。

  實際開發中,通常會提供 org.apache.shiro.realm.AuthenticatingRealm 的實現類,並在該實現類中提供 doGetAuthenticationInfo(AuthenticationToken token)方法的具體實現

    1、檢查提交的進行認證的令牌信息

    2、根據令牌信息從數據源(通常為數據庫)中獲取用戶信息

    3、對用戶信息進行匹配驗證。

    4、驗證通過將返回一個封裝了用戶信息的 AuthenticationInfo 實例。

    5、驗證失敗則拋出 AuthenticationException 異常信息。

 

 Spring 集成 Shiro

(本次[第一篇以理論為主]以非Maven的Web的項目示例,在我之后[以實戰為主]的“apache-shiro入門”系列我會以Maven項目示例)

  1. 加入相關 jar 包

  2. 配置 web.xml 文件   

    配置啟動 Spring IOC 容器的 Filter 

    配置 WEB 應用中的 Shiro Filter

 

  3. 在 Spring 的配置文件中配置 Shiro

 

      配置自定義 Realm:實現自定義認證和授權

    配置 Shiro 實體類使用的緩存策略

    配置 SecurityManager

    配置保證 Shiro 內部 Bean 聲明周期都得到執行的 Lifecycle Bean 后置處理器

    配置AOP 式方法級權限檢查

    配置 Shiro Filter   

      filterChainDefinitions 屬性:設置 Shiro Filter 攔截的 URL 及訪問該 URL 所需要的權限信息。

      格式:URL_Ant_Path_Expression = Path_Specific_Filter_Chain。

      等號左邊是一個與 Web 應用程序上下文根目錄相關的Ant 風格的路徑表達式。

      等號右邊是逗號隔開的過濾器列表,用來執行匹配該路徑的請求

      Ant 風格資源地址支持 3 種匹配符:

        ?:匹配文件名中的一個字符

        *:匹配文件名中的任意字符

        **:** 匹配多層路徑

    路徑表達式    

      URL 權限采取第一次匹配優先的方式

    比如:

      /account/** = ssl, authc

      /account/signup = anon

      如果傳入的請求訪問是 /account/signup/index.html,則會匹配 ssl, authc 權限,而 annon 將永不會被匹配!因為/account/** 匹配 短路了其余的權限定義

    權限信息    

      權限是用逗號隔開的過濾器列表,用來執行對該路徑請求的匹配。

      必須符合以下格式:filter1[optional_config1], filter2[optional_config2],...

      filterN:一個 shiro 中的過濾器的別名

      [optional_configN] :可選的權限字符串。若該過濾器對該 URL 路徑不需要特定的配置,可以忽略括號,於是 filteN[] 就變成了filterN.

shiro中默認的過濾器

 

 

 

  authc 和 user 

     user 和 authc :當應用開啟了rememberMe 時,用戶下次訪問時是一個 user,但不是 authc,因為authc是需要重新認證的 

    user 表示用戶不一定已通過認證,只要被 Shiro 記住過登錄狀態的用戶就可以正常發起請求,比如rememberMe 

    簡言之:以前的一個用戶登錄時開啟了rememberMe,然后他關閉瀏覽器,下次再訪問時他就是一個user,而不是authc 

  Remembered 和 Authenticated 

    Remembered(記住我):
      一個 記住我 的 Subject 不是匿名的,是有一個已知的身份ID(也就是subject.getPrincipals()是非空的)。即:這個被記住的身份 ID 是在之前的session 中被認證的。
      如果 subject.isRemembered() 返回 true,則 Subject 被認為是被記住的。
    Authenticated(已認證):
      一個已認證的 Subject 是指在當前 Session 中被成功地驗證過:login方法被調用並且沒有拋出異常
      如果 subject.isAuthenticated()返回 true 則認為 Subject 已通過驗證。
    注意:Remembered 和Authenticated 是互斥的——若其中一個為真則另一個為假,反之亦然

 

基於注解的授權

 

Shiro提供的注解

  @RequiresAuthentication :要求當前 Subject 已經在當前的session 中被驗證通過才能被注解的類/實例/方法訪問或調用。

  @RequiresGuest :要求當前的 Subject 是一個 “guest”,也就是他們必須是在之前的 session 中沒有被驗證或記住才能被注解的類/實例/方法訪問或調用。

  @RequiresPermissions:要求當前的 Subject 被允許一個或多個權限,以便執行注解的方法,比如:@RequiresPermissions("account:create")

  @RequiresRoles:要求當前的 Subject 擁有所有指定的角色。如果他們沒有,則該方法將不會被執行,而且 AuthorizationException 異常將會被拋出。比如:@RequiresRoles("administrator")

  @RequiresUser:需要當前的Subject 是一個應用程序用戶才能被注解的類/實例/方法訪問或調用。要么是通過驗證被確認,或者在之前session 中的 'RememberMe‘ 服務被記住。

 

 

 

 

基於標簽庫的授權:guest

  guest 標簽將顯示它包含的內容,僅當當前的Subject 被認為是 guest 時。

  guest 是指沒有身份 ID 的任何 Subject:沒有登錄也沒有在上一次的訪問中被記住(RememberMe 服務)

  guest 標簽與user 標簽邏輯相反。

  user 標簽將顯示它包含的內容,僅當當前的 Subject 被認為是 user 時。

  User 在上下文中被定義為一個已知身份 ID 的 Subject,或是成功通過身份驗證及通過 RememberMe 服務的。

  該標簽在語義上與authenticated 標簽是不同的,authenticated 標簽更為嚴格。

  usre 標簽與guest 標簽邏輯相反。

  authenticated:當當前用戶在當前會話中成功地通過了身份驗證 authenticated 標簽才會顯示包含的內容。比 user 標簽更為嚴格。在邏輯上與 notAuthenticated 標簽相反。

  notAuthenticated:當前 Subject 還沒有在其當前會話中成功地通過驗證,將會顯示它所包含的內容

  principal 標簽將會輸出Subject 的主體(標識屬性)或主要的屬性

  hasRole 當當前Subject 被分配了具體的角色時顯示它所包含的內容。hasRole 標簽與lacksRole 標簽邏輯相反。

  lacksRole 標簽:當當前Subject 未被分配具體的角色,顯示它所包含的內容

  hasAnyRole 標簽:當前的 Subject 被分配了任意一個來自於逗號分隔的角色名列表中的具體角色,將會顯示它所包含的內容。例如:

    <shiro:hasAnyRoles name="developer, project manager, administrator">

    You are either a developer, project manager, or administrater.

    </shiro:hasAnyRoles>

  hasPermission 標簽:當前 Subject 擁有特定的權限時,會顯示它所包含的內容。hasPermission 標簽與 lacksPermission 標簽邏輯相反。例如:

  <shiro:hasPermission name="user:create">

    <a href="createUser.jsp">Create a new User</a>

  </shiro:hasPermission>

  lacksPermission 標簽:當前Subject 沒有擁有(蘊含)特定的權限,將會顯示它所包含的內容。也就是說,用戶沒有特定的能力。

密碼加密

 

  1. 在 Realm 的 init 方法中設置憑證匹配器

  2. 認證時返回的 SimpleAuthenticationInfo 包含的密碼若是需要是經過加密后的密碼。若有鹽值,需要包含 credentialsSalt 參數

 

 

   3. 向數據庫中保存密碼時需要保存加密后的密碼

 

 

 

 

 

轉載請注明出處!

http://www.cnblogs.com/libingbin/

感謝您的閱讀。如果文章對您有用,那么請輕輕點個贊,以資鼓勵。

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM