Shiro學習筆記(跟Spring整合)


https://download.csdn.net/download/pz0605/10296661
Shrio與Spring的融合步驟:
1,配置ShiroFilterFactoryBean
2. 配置 CacheManager.
2.1 需要加入 ehcache 的 jar 包及配置文件.
3. 配置 Realm
3.1 直接配置實現了 org.apache.shiro.realm.Realm 接口的 bean
4. 配置 LifecycleBeanPostProcessor. 可以自動的來調用配置在 Spring IOC 容器中 shiro bean 的生命週期方法.
5. 啓用 IOC 容器中使用 shiro 的註解. 但必須在配置了 LifecycleBeanPostProcessor 之後纔可以使用.
6. 配置 ShiroFilter.
6.1 ShiroFilter bean的 id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
    若不一致, 則會拋出: NoSuchBeanDefinitionException. 因爲 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter bean.

配置web.xml與spring的配置文件時,要特別注意web.xml 中的DelegatingFilterProxy <filter-name> 要與Spring中
ShiroFilterFactoryBean中的bean的id保持一樣;

Shrio工作流程:
瀏覽器 => shiroFilter => 定位到成功或失敗頁面


Shrio配置受保護的URL:
    ?:匹配一個字符,如 /admin? 將匹配 /admin1,但不 匹配 /admin 或 /admin/;
    *:匹配零個或多個字符串,如 /admin 將匹配 /admin、 /admin123,但不匹配 /admin/1;
    **:匹配路徑中的零個或多個路徑,如 /admin/** 將匹配 /admin/a 或 /admin/a/b

    URL匹配原則:第一次匹配優先,所以越是模糊的匹配就該越放到後邊


Shrio認證(登陸)流程:

   1. 獲取當前的 Subject. 調用 SecurityUtils.getSubject();
   2. 測試當前的用戶是否已經被認證. 即是否已經登錄. 調用 Subject 的 isAuthenticated()
   3. 若沒有被認證, 則把用戶名和密碼封裝爲 UsernamePasswordToken 對象

   如何從數據庫中獲取用戶記錄?使用Realm,具體步驟如下:
   1). 創建一個表單頁面
   2). 把表單的用戶名與密碼請求提交到 SpringMVC 的 Handler
   3). 在Handler中獲取用戶名和密碼.
   4). 執行登錄: 調用 Subject 的 login(AuthenticationToken) 方法.
   5). 自定義 Realm 的方法, 從數據庫中獲取對應的記錄, 返回給 Shiro.
     如何自定義Realm
     5.1). 實際上需要繼承 org.apache.shiro.realm.AuthenticatingRealm 類
     5.2). 實現AuthorizingRealm接口中doGetAuthenticationInfo(AuthenticationToken) 方法.
   6. 由 shiro 完成對密碼的比對.

    org.apache.shiro.subject.support.DelegatingSubject.login()

自定義的AuthorizingRealm主要邏輯:
   1,實現doGetAuthenticationInfo(AuthenticationToken token)用於認證
   2,把 AuthenticationToken 轉換爲 UsernamePasswordToken
      UsernamePasswordCaptchaToken token = (UsernamePasswordCaptchaToken)authcToken;
   3,從數據庫中獲取用戶信息
      User user = this.userService.findUserByLoginName(token.getUsername());
   4,判斷用戶是否存在、賬號是否禁用等
   5,根據用戶的情況, 來構建 AuthenticationInfo 對象並返回. 通常使用的實現類爲: SimpleAuthenticationInfo
      以下信息是從數據庫中獲取的.
     1). principal: 認證的實體信息. 可以是 username, 也可以是數據表對應的用戶的實體類對象.
     2). credentials: 密碼.
     3). realmName: 當前 realm 對象的 name. 調用父類的 getName() 方法即可
     4). 鹽值加密.

Shrio密碼比對:
通過 AuthenticatingRealm 的 credentialsMatcher.doCredentialsMatch 方法來進行的密碼的比對!

Shrio MD5 鹽值加密,爲什麼使用 MD5 鹽值加密?
如何實現MD5加密:
 1). 在 doGetAuthenticationInfo 方法返回值創建 SimpleAuthenticationInfo 對象的時候, 需要使用
 SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 構造器
 2). 使用 ByteSource.Util.bytes() 來計算鹽值.
 3). 鹽值需要唯一: 一般使用隨機字符串或 user id
 4). 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 來計算鹽值加密後的密碼的值.


Shrio多Realm驗證:
   1,配置ModularRealmAuthenticator
       <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
           <property name="authenticationStrategy">
               <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/>
           </property>
       </bean>
   2,配置realms
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
        <property name="realms">
           <list>
             <ref bean="jdbcRealm"/>
             <ref bean="secondRealm"/>
          </list>
        </property>
        <property name="rememberMeManager.cookie.maxAge" value="10"></property>
    </bean>

    注意:realms中list有先後順序

多個Realm的認證策略:
    ModularRealmAuthenticator.doMultiRealmAuthentication
    FirstSuccessfulStrategy:只要有一個 Realm 驗證成功即可,只返回第 一個 Realm 身份驗證成功的認證信息,其他的忽略;
    AtLeastOneSuccessfulStrategy:只要有一個Realm驗證成功即可,和 FirstSuccessfulStrategy 不同,將返回所有Realm身份驗證成功的認證信 息;
    AllSuccessfulStrategy:所有Realm驗證成功纔算成功,且返回所有 Realm身份驗證成功的認證信息,如果有一個失敗就失敗了。
    ModularRealmAuthenticator 默認是 AtLeastOneSuccessfulStrategy 策略, 默認的認證策略有一個就OK了

認證策略的配置:
    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
             <bean class="org.apache.shiro.authc.pam.FirstSuccessfulStrategy"/><!--第一個認證成功即可-->
        </property>
    </bean>

Realm授權:
   也叫訪問控制,即在應用中控制誰訪問哪些資源(如訪問頁面/編輯數據/頁面操作 等)。
   在授權中需瞭解的幾個關鍵對象:主體(Subject)、資源(Resource)、權限 (Permission)、角色(Role)。
   三種授權方式:
   編程式:
   註解式:
   JSP/GSP標籤:

   默認攔截器可以參考:org.apache.shiro.web.filter.mgt.DefaultFilter
   或 http://blog.csdn.net/sunqingzhong44/article/details/68961199


授權的Realm如何實現?
   1. 授權需要繼承 AuthorizingRealm 類, 並實現其 doGetAuthorizationInfo 方法
   2. AuthorizingRealm 類繼承自 AuthenticatingRealm, 但沒有實現 AuthenticatingRealm 中的
    doGetAuthenticationInfo, 所以認證和授權只需要繼承 AuthorizingRealm 就可以了. 同時實現他的兩個抽象方法.

  參考代碼:
   //授權會被 shiro 回調的方法
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(
         PrincipalCollection principals) {
      //1. 從 PrincipalCollection 中來獲取登錄用戶的信息
      Object principal = principals.getPrimaryPrincipal();

      //2. 利用登錄的用戶的信息來用戶當前用戶的角色或權限(可能需要查詢數據庫)
      Set<String> roles = new HashSet<>();
      roles.add("user");
      if("admin".equals(principal)){
         roles.add("admin");
      }

      //3. 創建 SimpleAuthorizationInfo, 並設置其 reles 屬性.
      SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

      //4. 返回 SimpleAuthorizationInfo 對象.
      return info;
   }


多Realm的授權的時候,怎麼纔算授權通過?只要有一個通過就算通過!


Shire標籤:
    guest 標籤:用戶沒有身份驗證時顯示相應信息,即遊客 訪問信息:
    user 標籤:用戶已經經過認證/記住我登錄後顯示相應的信 息。
    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體內容。

    使用的時候要導入:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

Shiro權限的註解:
    @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。

   比如:
   public class ShiroService {
          @RequiresRoles({"admin"})
       public void testMethod(){
             System.out.println("testMethod, time: " + new Date());
             Session session = SecurityUtils.getSubject().getSession();
             Object val = session.getAttribute("key");
             System.out.println("Service SessionVal: " + val);
       }
   }

   在控制器中:
   @RequestMapping("/testShiroAnnotation")
   public String testShiroAnnotation(HttpSession session){
       session.setAttribute("key", "value12345");
          shiroService.testMethod();//沒有admin權限,將會拋出錯誤會
          return "redirect:/list.jsp";
   }

Shiro資源與權限從數據庫中獲取:
   1,配置一個 bean, 該 bean 實際上是一個 Map. 通過實例工廠方法的方式 -->
     <bean id="filterChainDefinitionMap"
       factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>
     <bean id="filterChainDefinitionMapBuilder"
          class="com.atguigu.shiro.factory.FilterChainDefinitionMapBuilder"></bean>
     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/list.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
     </bean>

   2,
   public class FilterChainDefinitionMapBuilder {
    //從數據庫中加載資源與權限
   public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){
      LinkedHashMap<String, String> map = new LinkedHashMap<>();
      map.put("/login.jsp", "anon");
      map.put("/shiro/login", "anon");
      map.put("/shiro/logout", "logout");
      map.put("/user.jsp", "authc,roles[user]");
      map.put("/admin.jsp", "authc,roles[admin]");
      map.put("/list.jsp", "user");
      map.put("/**", "authc");
      return map;
   }
  }


Shiro會話管理:
   會話相關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() 來更新最後訪問時間


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

認證和記住我
   subject.isAuthenticated() 表示用戶進行了身份驗證登錄的,即使有 Subject.login 進行了登錄;
   subject.isRemembered():表示用戶是通過記住我登錄的, 此時可能並不是真正的你(如你的朋友使用你的電腦,或者 你的cookie 被竊取)在訪問的
   兩者二選一,即 subject.isAuthenticated()==true,則 subject.isRemembered()==false;反之一樣。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章