日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Shiro学习笔记

發布時間:2023/12/13 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Shiro学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Shiro教學視頻

源碼

文章目錄

    • 1. 簡介
      • 功能簡介
      • Shiro 架構
        • Shiro 架構(Shiro外部來看)
        • Shiro 架構(Shiro內部來看)
    • 2. HelloWorld
      • shiro.ini配置文件解析
      • Quickstart.java解析
        • 運行結果
    • 3. 集成Spring
      • 配置web.xml
      • 配置spring-servlet.xml
      • 配置applicationContext.xml
    • 4. Shiro工作流程
    • 5. DelegatingFilterProxy
    • 6. 權限URL配置細節
      • URL 匹配模式
      • Shiro中默認的過濾器
      • URL 匹配順序
    • 7. 認證思路分析
    • 8. 實現認證流程
    • 9. 實現認證Realm
    • 10. 密碼的比對
    • 11. 密碼的MD5加密
    • 12. 密碼的MD5鹽值加密
    • 13. 多Realm驗證
    • 14. 認證策略
    • 15. 把realms配置給SecurityManager
    • 16. 權限配置
      • Shiro 支持三種方式的授權
      • 默認攔截器
        • 身份驗證相關的攔截器
        • 授權相關的攔截器
        • 其他的攔截器
      • 實操
    • 17. 授權流程分析
    • 18. 多Realm授權的通過標準
    • 19. 實現授權Realm
    • 20. 標簽
    • 21. 權限注解
    • 22. 從數據表中初始化資源和權限
    • 23. 會話管理
      • Session會話相關的 API
    • 24. SessionDao
    • 25. 緩存
      • CacheManagerAware 接口
      • Realm 緩存
      • Session 緩存
    • 26. 認證和記住我的區別
    • 27. 實現Remember me

1. 簡介

  • Apache Shiro 是 Java 的一個安全(權限)框架。
  • Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE 環境,也可以用在 JavaEE 環境。
  • Shiro 可以完成:認證、授權、加密、會話管理、與Web集成、緩存等。
  • Shiro官網

功能簡介

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

authentication /???θent??ke??n/n. 身份驗證;認證;鑒定

authorization /???θ?r??ze??n/n. 批準;授權;批準書;授權書

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內部來看)

2. HelloWorld

  • 新鍵Maven Web工程。
  • pom.xml加入依賴:
  • org.apache.shiro : shiro-all : 1.3.2
  • org.slf4j : slf4j-log4j12 : 1.8.0-alpha2
  • 添加配置文件:
  • shiro.ini Shiro的配置文件
  • log4j.properties 日志配置
  • 添加Shiro的HelloWorld類——Quickstart.java
  • shiro.ini配置文件解析

    shiro.ini配置文件主要內容為:

  • 用戶=密碼, 角色
  • 角色=角色對應的權限
  • # ----------------------------------------------------------------------------- # 用戶=密碼, 角色 # Users and their assigned roles ----------------------------------------------------------------------------- [users] # 1. user 'root' with password 'secret' and the 'admin' role root = secret, admin# 2. user 'guest' with the password 'guest' and the 'guest' role guest = guest, guest# 3. user 'presidentskroob' with password '12345' ("That's the same combination on # my luggage!!!" ;)), and role 'president' presidentskroob = 12345, president# 4. user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz' darkhelmet = ludicrousspeed, darklord, schwartz# 5. user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz' lonestarr = vespa, goodguy, schwartz# ----------------------------------------------------------------------------- # Roles with assigned permissions # 角色以及它的權限 ----------------------------------------------------------------------------- [roles] # 1. 'admin' role has all permissions, indicated by the wildcard '*' admin = *# 2. The 'schwartz' role can do anything (*) with any lightsaber: schwartz = lightsaber:*# 3. The 'goodguy' role is allowed to 'delete' (action) the user (type) with # license plate 'zhangsan' (instance specific id) goodguy = user:delete:zhangsan

    Quickstart.java解析

    從Quickstart.java中,大概可看出如何使用Shiro進行認證、授權等安全操作。

    public class Quickstart {public static void main(String[] args) {//創建并安全管理器,需要用到上述的shiro.ini配置文件Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);//獲取當前的Subject,這里Subject可理解為即將登陸的用戶Subject currentUser = SecurityUtils.getSubject();// 測試使用Session讀寫鍵值對,這個Session無需Web容器就可使用Session session = currentUser.getSession();session.setAttribute("someKey", "aValue");String value = (String) session.getAttribute("someKey");if (value.equals("aValue")) {log.info("---> Retrieved the correct value! [" + value + "]");}// 測試當前的用戶是否已經被認證,即是否已經登錄if (!currentUser.isAuthenticated()) {// 把用戶名和密碼封裝為 UsernamePasswordToken 對象UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");// remembermetoken.setRememberMe(true);try {//執行登錄. currentUser.login(token);} // 若沒有指定的賬戶,則拋出 UnknownAccountExceptioncatch (UnknownAccountException uae) {log.info("----> There is no user with username of " + token.getPrincipal());return; } // 若賬戶存在,但密碼不匹配,則拋出 IncorrectCredentialsException 異常catch (IncorrectCredentialsException ice) {log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");return; } // 用戶被鎖定的異常 LockedAccountExceptioncatch (LockedAccountException lae) {log.info("The account for username " + token.getPrincipal() + " is locked. " +"Please contact your administrator to unlock it.");}// 所有認證時異常的父類AuthenticationException,也就是說LockedAccountException、IncorrectCredentialsException等繼承該父類catch (AuthenticationException ae) {//unexpected condition? error?}}//print their identifying principal (in this case, a username):log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");// 測試是否有某一個角色if (currentUser.hasRole("schwartz")) {log.info("----> May the Schwartz be with you!");} else {log.info("----> Hello, mere mortal.");return; }// 測試用戶是否具備某一個行為if (currentUser.isPermitted("lightsaber:weild")) {log.info("----> You may use a lightsaber ring. Use it wisely.");} else {log.info("Sorry, lightsaber rings are for schwartz masters only.");}//測試用戶是否具備某一個更具體的行為:if (currentUser.isPermitted("user:delete:zhangsan")) {log.info("----> You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +"Here are the keys - have fun!");} else {log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");}// 執行登出log.info("---->currentUser.isAuthenticated(): " + currentUser.isAuthenticated());currentUser.logout();log.info("---->currentUser.isAuthenticated(): " + currentUser.isAuthenticated());System.exit(0);} }

    principal [?pr?ns?pl]
    adj. 最重要的;主要的
    n. 大學校長;學院院長;本金;資本;主要演員;主角

    運行結果

    2020-07-11 18:43:05,687 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler... 2020-07-11 18:43:06,051 INFO [com.lun.shiro.one.Quickstart] - Retrieved the correct value! [aValue] 2020-07-11 18:43:06,054 INFO [com.lun.shiro.one.Quickstart] - User [lonestarr] logged in successfully. 2020-07-11 18:43:06,055 INFO [com.lun.shiro.one.Quickstart] - May the Schwartz be with you! 2020-07-11 18:43:06,056 INFO [com.lun.shiro.one.Quickstart] - You may use a lightsaber ring. Use it wisely. 2020-07-11 18:43:06,056 INFO [com.lun.shiro.one.Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. Here are the keys - have fun! 2020-07-11 18:43:06,056 INFO [com.lun.shiro.one.Quickstart] - currentUser.isAuthenticated(): true 2020-07-11 18:43:06,059 INFO [com.lun.shiro.one.Quickstart] - currentUser.isAuthenticated(): false

    3. 集成Spring

  • 在已有的Maven工程的pom.xml加入Spring框架全家桶依賴
  • 添加配置文件
  • web.xml
  • applicationContext.xml
  • spring-servlet.xml
  • 配置web.xml

    配置web.xml主要目的:

  • 引入Spring - org.springframework.web.context.ContextLoaderListener
  • 引入SpringMVC - org.springframework.web.servlet.DispatcherServlet
  • 引入Shiro
  • <web-app ...><!-- 1. 引入Spring - Bootstraps the root web application context before servlet initialization --><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- needed for ContextLoaderListener --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><!-- 2. 引入Spring MVC - The front controller of this Spring Web application, responsible for handling all application requests --><servlet><servlet-name>spring</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><!-- Map all requests to the DispatcherServlet for handling --><servlet-mapping><servlet-name>spring</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 3. 引入Shiro - Shiro Filter is defined in the spring application context: --><!-- DelegatingFilterProxy 實際上是 Filter 的一個代理對象. 默認情況下, Spring 會到 IOC 容器中查找和 <filter-name> 對應的 filter bean. 也可以通過 targetBeanName 的初始化參數來配置 filter bean 的 id. --><filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
    • Shiro 提供了與 Web 集成的支持,其通過一個ShiroFilter 入口來攔截需要安全控制的URL,然后進行相應的控制
    • ShiroFilter 類似于如 Strut2/SpringMVC 這種web 框架的前端控制器,是安全控制的入口點,其負責讀取配置(如ini 配置文件),然后判斷URL是否需要登錄/權限等工作。

    配置spring-servlet.xml

    在spring-servlet.xml配置SpringMVC信息

    <beans ...><context:component-scan base-package="com.lun"></context:component-scan><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/"></property><property name="suffix" value=".jsp"></property></bean><mvc:annotation-driven></mvc:annotation-driven><mvc:default-servlet-handler/> </beans>

    配置applicationContext.xml

    Shiro的配置信心主要在applicationContext.xml中。

  • 配置 SecurityManager
  • 配置 CacheManager
  • 配置 Realm
  • 配置 LifecycleBeanPostProcessor. 可以自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命周期方法
  • 啟用 IOC 容器中使用 shiro 的注解. 但必須在配置了 LifecycleBeanPostProcessor 之后才可以使用
  • 配置 ShiroFilter
  • <beans ...><!-- 1. 配置 SecurityManager --> <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><!-- 2. 配置 CacheManager,用作緩存管理--><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"><property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean><!-- 3. 配置 Realm --> <bean id="jdbcRealm" class="com.lun.shiro.realms.ShiroRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="MD5"></property><property name="hashIterations" value="1024"></property></bean></property></bean><bean id="secondRealm" class="com.lun.shiro.realms.SecondRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="SHA1"></property><property name="hashIterations" value="1024"></property></bean></property></bean><!-- 多個Realm時,配置處理策略<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"><property name="authenticationStrategy"><bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean></property></bean><!-- 4. 配置 LifecycleBeanPostProcessor. 可以自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. --><bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/><!-- 5. 啟用 IOC 容器中使用 shiro 的注解. 但必須在配置了 LifecycleBeanPostProcessor 之后才可以使用. --><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"depends-on="lifecycleBeanPostProcessor"/><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/></bean><!-- 6. 配置 ShiroFilter. id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.若不一致, 則會拋出: NoSuchBeanDefinitionException.因為 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter 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> --><!-- 配置哪些頁面需要受保護. 以及訪問這些頁面需要的權限. 1). anon 可以被匿名訪問2). authc 必須認證(即登錄)后才可能訪問的頁面. 3). logout 登出.4). roles 角色過濾器--><property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout/user.jsp = roles[user]/admin.jsp = roles[admin]# everything else requires authentication:/** = authc</value></property></bean><!-- 配置一個 bean, 該 bean 實際上是一個 Map. 通過實例工廠方法的方式 --><bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"/><bean id="filterChainDefinitionMapBuilder" class="com.lun.shiro.factory.FilterChainDefinitionMapBuilder"/><bean id="shiroService" class="com.lun.shiro.services.ShiroService"/></beans>

    4. Shiro工作流程

    5. DelegatingFilterProxy

    在web.xml中

    <!-- 配置 Shiro 的 shiroFilterDelegatingFilterProxy 實際上是 Filter 的一個代理對象。 默認情況下, Spring 會到 IOC 容器中查找和 <filter-name> 對應的 filter bean。也可以通過 targetBeanName 的初始化參數來配置 filter bean 的 id。 --> <filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param><!-- <init-param><param-name>targetBeanName</param-name><param-value>abc</param-value>也可以重新命名,但通常沒這個必要</init-param>--> </filter>

    在applicationContext.xml中

    ... <!-- 6. 配置 ShiroFilter. id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.若不一致, 則會拋出: NoSuchBeanDefinitionException.因為 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter bean. --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager"/>...</bean> ...

    6. 權限URL配置細節

    [urls]部分的配置格式是:“url=攔截器[參數],攔截器[參數]”。

    如果當前請求的 url 匹配 [urls] 部分的某個 url 模式,將會執行其配置的攔截器。

    攔截器例子如下:

    • anon(anonymous)攔截器表示匿名訪問(即不需要登錄即可訪問)
    • authc(authentication)攔截器表示需要身份認證通過后才能訪問
    <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"/><!-- 配置哪些頁面需要受保護. 以及訪問這些頁面需要的權限. 1). anon 可以被匿名訪問2). authc 必須認證(即登錄)后才可能訪問的頁面. 3). logout 登出.4). roles 角色過濾器--><property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout/user.jsp = roles[user]/admin.jsp = roles[admin]# everything else requires authentication:/** = authc</value></property> </bean>

    URL 匹配模式

    url 模式使用 Ant 風格模式

    Ant 路徑通配符支持 ?、 *、 **,注意通配符匹配不包括目錄分隔符“/”

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

    Shiro中默認的過濾器

    Default Filters

    URL 匹配順序

    URL 權限采取第一次匹配優先的方式, 即從頭開始使用第一個匹配的 url 模式對應的攔截器鏈。如:

    • /bb/**=filter1
    • /bb/aa=filter2
    • /**=filter3

    如果請求的url是“/bb/aa”,因為按照聲明順序進行匹配,那么將使用 filter1 進行攔截。(先聲奪人)

    7. 認證思路分析

    認證說白就是登陸

  • 獲取當前的Subject,調用SecurityUtils.getSubject()
  • 測試當前的用戶是否已經被認證,即是否已經登錄。調用 Subject.isAuthenticated()
  • 若沒有被認證,則把用戶名和密碼封裝為 UsernamePasswordToken 對象
  • 創建一個表單頁面
  • 把請求提交到SpringMVC的Handler
  • 獲取用戶名和密碼.
  • 執行登錄:調用Subject的login(AuthenticationToken)方法
  • 自定義Realm的方法,從數據庫中獲取對應的記錄,返回給Shiro.
  • 實際上需要繼承org.apache.shiro.realm.AuthenticatingRealm類
  • 實現doGetAuthenticationInfo(AuthenticationToken)方法.
  • 由Shiro完成對密碼的比對。
  • 8. 實現認證流程

  • 創建表單頁面login.jsp
  • 創建表單頁面Controller - ShiroHandler
  • 創建Realm - ShiroRealm,從數據庫中獲取對應的記錄,返回給 Shiro.
  • 實際上需要繼承 org.apache.shiro.realm.AuthenticatingRealm 類
  • 實現 doGetAuthenticationInfo(AuthenticationToken) 方法,添加賬號,密碼驗證等相關代碼。
  • 9. 實現認證Realm

  • 把 AuthenticationToken 轉換為 UsernamePasswordToken
  • 從 UsernamePasswordToken 中來獲取 username
  • 調用數據庫的方法, 從數據庫中查詢 username 對應的用戶記錄
  • 若用戶不存在, 則可以拋出 UnknownAccountException 異常
  • 根據用戶信息的情況, 決定是否需要拋出其他的 AuthenticationException 異常.
  • 根據用戶的情況, 來構建 AuthenticationInfo 對象并返回。通常使用的實現類為: SimpleAuthenticationInfo,以下信息是從數據庫中獲取的
  • principal: 認證的實體信息. 可以是 username, 也可以是數據表對應的用戶的實體類對象。
  • credentials: 密碼
  • realmName: 當前 realm 對象的 name. 調用父類的 getName() 方法即可
  • ShiroRealm

    public class ShiroRealm extends AuthorizingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("[FirstRealm] doGetAuthenticationInfo");//1. 把 AuthenticationToken 轉換為 UsernamePasswordToken UsernamePasswordToken upToken = (UsernamePasswordToken) token;//2. 從 UsernamePasswordToken 中來獲取 usernameString username = upToken.getUsername();//3. 調用數據庫的方法, 從數據庫中查詢 username 對應的用戶記錄System.out.println("從數據庫中獲取 username: " + username + " 所對應的用戶信息.");//4. 若用戶不存在, 則可以拋出 UnknownAccountException 異常if("unknown".equals(username)){throw new UnknownAccountException("用戶不存在!");}//5. 根據用戶信息的情況, 決定是否需要拋出其他的 AuthenticationException 異常. if("monster".equals(username)){throw new LockedAccountException("用戶被鎖定");}//6. 根據用戶的情況, 來構建 AuthenticationInfo 對象并返回. 通常使用的實現類為: SimpleAuthenticationInfo//以下信息是從數據庫中獲取的.//1). principal: 認證的實體信息. 可以是 username, 也可以是數據表對應的用戶的實體類對象. Object principal = username;//2). credentials: 密碼. Object credentials = "123456";//3). realmName: 當前 realm 對象的 name. 調用父類的 getName() 方法即可String realmName = getName();SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);return info;} }

    10. 密碼的比對

    密碼的比對:通過 AuthenticatingRealm 的 credentialsMatcher 屬性來進行的密碼的比對。

    11. 密碼的MD5加密

    如何把一個字符串加密為 MD5

    替換當前 Realm 的 credentialsMatcher 屬性。直接使用 HashedCredentialsMatcher 對象, 并設置加密算法即可。

    在applicationContext.xml中

    <!-- 3. 配置 Realm,直接配置實現了 org.apache.shiro.realm.Realm 接口的 bean --><bean id="jdbcRealm" class="com.lun.shiro.realms.ShiroRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="MD5"></property><property name="hashIterations" value="1024"></property></bean></property></bean>

    既然對密碼了,ShiroRealm中的部分代碼稍作修改。

    計算求出密碼加密后的密碼

    public static void main(String[] args) {String hashAlgorithmName = "MD5";Object credentials = "123456";Object salt = null;//鹽值下一節將提到int hashIterations = 1024;Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);System.out.println(result); }

    得出fc1709d0a95a6be30bc5926fdb7f22f4

    然后在ShiroRealm中的

    Object credentials = "123456";

    改為

    Object credentials = "fc1709d0a95a6be30bc5926fdb7f22f4";

    便可實現密碼加密,通過驗證。

    12. 密碼的MD5鹽值加密

    為什么使用 MD5 鹽值加密: 因為有時希望即使兩個原始密碼一樣,加密后的密碼也不一樣,這樣做相對安全些。

    鹽是日常生活中的調料。

    如何做到:

  • 在 doGetAuthenticationInfo 方法返回值創建 SimpleAuthenticationInfo 對象的時候,需要使用SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)構造器
  • 使用ByteSource.Util.bytes()來計算鹽值。
  • 鹽值需要唯一:一般使用隨機字符串或 user id
  • 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);來計算鹽值加密后的密碼的值。
  • 然后計算密碼加密加鹽的值

    public static void main(String[] args) {String hashAlgorithmName = "MD5";Object credentials = "123456";Object salt = ByteSource.Util.bytes("user");int hashIterations = 1024;Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);System.out.println(result); }

    在ShiroRealm進行對應的修改

    @Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {....//2). credentials: 密碼. Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";if("admin".equals(username)){credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";/*上面main()計算得出加密加鹽的值*/}else if("user".equals(username)){credentials = "098d2c478e9c11555ce2823231e02ec1";/*上面main()計算得出加密加鹽的值*/}//3). realmName: 當前 realm 對象的 name. 調用父類的 getName() 方法即可String realmName = getName();//4). 鹽值. ByteSource credentialsSalt = ByteSource.Util.bytes(username);SimpleAuthenticationInfo info = null; info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);return info; }

    13. 多Realm驗證

    realm /relm/ n. 領域; 場所; 王國

    可設置多個Realm設置

    applicationContext.xml

    <!-- 1. 配置 SecurityManager --> <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><!-- 多Realm是要添加的多個認真策略 --> <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"><property name="authenticationStrategy"><bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean></property> </bean>

    創建添加第二個Realm,SecondRealm

    applicationContext.xml實現SecondRealm的Bean

    <bean id="secondRealm" class="com.lun.shiro.realms.SecondRealm"><property name="credentialsMatcher"><bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><property name="hashAlgorithmName" value="SHA1"></property><property name="hashIterations" value="1024"></property></bean></property> </bean>

    14. 認證策略

    AuthenticationStrategy 接口的默認實現:

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

    15. 把realms配置給SecurityManager

    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"><property name="authenticationStrategy"><bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean></property><property name="realms"><list><ref bean="jdbcRealm"/><ref bean="secondRealm"/></list></property> </bean>

    中的property name="realms"部分可以移動到bean id=“securityManager” 中,原因在查閱源碼中,securityManager 的authenticator是ModularRealmAuthenticator類的實例,就將securityManager的realms注入authenticator當中。

    <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>

    這樣做可以放心為下一節授權做鋪墊。

    16. 權限配置

    • 授權,也叫訪問控制,即在應用中控制誰訪問哪些資源(如訪問頁面/編輯數據/頁面操作等)。在授權中需了解的幾個關鍵對象:主體( 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中的枚舉攔截器:

    Default Filters

    身份驗證相關的攔截器

    默認攔截器名攔截器類說明(括號里的表示默認值)
    authcorg.apache. shiro.web.filter.authc.FormAuthenticationFilter基于表單的攔截器;如"/**=authc" ,如果沒有登錄會跳到相應的登錄頁面登錄;主要屬性:
    usernameParam :表單提交的用戶名參數名(username);
    passwordParam :表單提交的密碼參數名(password);
    rememberMeParam :表單提交的密碼參數名(rememberMe);
    loginUrl :登錄頁面地址(/login.jsp);
    successUrl :登錄成功后的默認重定向地址;
    failureKeyAttribute :登錄失敗后錯誤信息存儲key( shirologinFailure);
    authcBasicorg.apache. shiro.web.filter.authc.BasicHttpAuthenticationFilterBasic HTTP身份驗證攔截器,主要屬性 :applicationName :彈出登錄框顯示的信息( application) ;
    logoutorg.apache.shiro.web.filter.authc.LogoutFilter退出攔截器,主要屬性: redirectUrl :退出成功后重定向的地址( / ) ;示例:"/logout=logout"
    userorg.apache.shiro.web.filter.authc.UserFilter用戶攔截器,用戶已經身份驗證/記住我登錄的都可;示例"/** =user"
    anonorg.apache.shiro.web.filter.authc.AnonymousFilter匿名攔截器,即不需要登錄即可訪問; -般用于靜態資源過濾;示例:/static/** = anon"

    授權相關的攔截器

    默認攔截器名攔截器類說明(括號里的表示默認值)
    rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter角色授權攔截器,驗證用戶是否擁有所有角色;主要屬性:loginUrl :登錄頁面地址(/login.jsp);unauthorizedUrl :未授權后重定向的地址;示例: “/admin/** =roles[admin]”
    permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter權限授權攔截器,驗證用戶是否擁有所有權限;屬性和roles一樣;示例"/user/**=perms[“user:create”]"
    portorg.apache.shiro.web.filter.authz.PortFilter端口攔截器,主要屬性: port(80) :可以通過的端口;示例"/test= port[80]如果用戶訪問該頁面是非80 ,將自動將請求端口改為80并重定向到該80端口,其他路徑/參數等都一樣
    restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilterrest風格攔截器,自動根據請求方法構建權限字符串( GET=read,POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS =read, MKCOL=create )構建權限字符串;示例:/users=rest[user]” ,會自動拼出"user:read,user:create,user:update,user:delete"權限字符串進行權限匹配(所有都得匹配, isPermittedAll);
    sslorg.apache.shiro.web.filter.authz.SslFilterSSL攔截器,只有請求協議是https才能通過;否則自動跳轉會https端口(443) ;其他和port攔截器一樣;

    其他的攔截器

    默認攔截器名攔截器類說明(括號里的表示默認值)
    noSessionCreationorg.apache.shiro.web.filter.session. NoSessionCreationFilter不創建會話攔截器,調用subject.getSession(false)不會有什么問題,但是如果subject.getSession(true)將拋出

    實操

    新建兩頁面

    • admin.jsp - 只可角色admin訪問的
    • user.jsp - 只可角色user訪問的

    在applicationContext.xml中進行配置

    <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"/><!-- 配置哪些頁面需要受保護. 以及訪問這些頁面需要的權限. 1). anon 可以被匿名訪問2). authc 必須認證(即登錄)后才可能訪問的頁面. 3). logout 登出.4). roles 角色過濾器--><property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout<!-- 本節關鍵這里 -->/user.jsp = roles[user]/admin.jsp = roles[admin]# everything else requires authentication:/** = authc</value></property> </bean>

    在ShiroRealm設置realmName字符串是用來設置角色

    public class ShiroRealm extends AuthorizingRealm {@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//...info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);//realmName是設置角色的return info;} }

    17. 授權流程分析

  • 授權需要繼承 AuthorizingRealm 類, 并實現其 doGetAuthorizationInfo 方法
  • AuthorizingRealm 類繼承自 AuthenticatingRealm, 但沒有實現 AuthenticatingRealm 中的 doGetAuthenticationInfo, 所以認證和授權只需要繼承 AuthorizingRealm 就可以了. 同時實現他的兩個抽象方法.
  • public class TestRealm extends AuthorizingRealm {//用于授權的方法. @Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// TODO Auto-generated method stubreturn null;}//用于認證的方法@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// TODO Auto-generated method stubreturn null;}}

    調用詳細流程可以通過Debug Quickstart的currentUser.hasRole("schwartz")進行了解

    18. 多Realm授權的通過標準

    調用詳細流程可以通過Debug Quickstart的currentUser.hasRole("schwartz")進行了解

    19. 實現授權Realm

    ShiroRealm

    public class ShiroRealm extends AuthorizingRealm {//...//授權會被 shiro 回調的方法@Overrideprotected 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;} }

    20. 標簽

    Shiro 提供了 JSTL 標簽用于在 JSP 頁面進行權限控制,如根據登錄用戶顯示相應的頁面按鈕。

    在JSP文件中引入時

    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    guest 標簽:用戶沒有身份驗證時顯示相應信息,即游客訪問信息:

    <shiro:guest>歡迎游客訪問</shiro:guest>

    user 標簽:用戶已經經過認證/記住我登錄后顯示相應的信息。

    <shiro:user>歡迎</shiro:user>

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

    <shiro:authenticated></shiro:authenticated>

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

    <shiro:notAuthenticated></shiro:notAuthenticated>

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

    <shiro:principal property="username"></shiro:principal>

    hasRole 標簽:如果當前 Subject 有角色將顯示 body 體內容:

    <shiro:hasRole name="admin"><a href="admin.jsp">Admin Page</a> </shiro:hasRole>

    hasAnyRoles 標簽:如果當前Subject有任意一個角色(或的關系)將顯示body體內容

    <shiro:hasAnyRoles name="user,admin"></shiro:hasAnyRoles>

    lacksRole:如果當前 Subject 沒有角色將顯示 body 體內容

    <shiro:lacksRole name="admin"></shiro:lacksRole>

    hasPermission: 如果當前 Subject 有權限將顯示 body 體內容

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

    lacksPermission: 如果當前Subject沒有權限將顯示body體內容。

    <shiro:lacksPermission name="org:create"></shiro:lacksPermission>

    21. 權限注解

    • @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。

    ShiroService

    public class ShiroService {@RequiresRoles({"admin"})//這里使用到權限注解,這里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);}}

    在applicaContext.xml實現Bean

    <bean id="shiroService"class="com.lun.shiro.services.ShiroService"></bean> @Controller @RequestMapping("/shiro") public class ShiroHandler {@Autowiredprivate ShiroService shiroService;@RequestMapping("/testShiroAnnotation")public String testShiroAnnotation(HttpSession session){session.setAttribute("key", "value12345");shiroService.testMethod();//該方法調用需要驗證權限,用到admin角色權限,否則,就會報錯return "redirect:/list.jsp";}//... }

    22. 從數據表中初始化資源和權限

    在applicationContext.xml硬編碼分配權限角色,這不太好。通常情況下,是需要從數據庫中讀取權限角色的。

    <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><!-- 配置哪些頁面需要受保護. 以及訪問這些頁面需要的權限. 1). anon 可以被匿名訪問2). authc 必須認證(即登錄)后才可能訪問的頁面. 3). logout 登出.4). roles 角色過濾器--> <!--<property name="filterChainDefinitions"><value>/login.jsp = anon/shiro/login = anon/shiro/logout = logout/user.jsp = roles[user]/admin.jsp = roles[admin]# everything else requires authentication:/** = authc</value></property>--> </bean>

    將property name="filterChainDefinitions"注釋掉,添加property name=“filterChainDefinitionMap” ref=“filterChainDefinitionMap”

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">...<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>... </bean>

    然后,在applicationContext.xml中注冊工廠方法Bean:

    <!-- 配置一個 bean, 該 bean 實際上是一個 Map. 通過實例工廠方法的方式 --> <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean><bean id="filterChainDefinitionMapBuilder"class="com.lun.shiro.factory.FilterChainDefinitionMapBuilder"></bean>

    然后實現工廠類FilterChainDefinitionMapBuilder.java

    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;}}

    23. 會話管理

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

    Session會話相關的 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):設置/獲取/刪除會話屬性;在整個會話范圍內都可以對這些屬性進行操作

    ShiroHandler

    @Controller @RequestMapping("/shiro") public class ShiroHandler {@Autowiredprivate ShiroService shiroService;@RequestMapping("/testShiroAnnotation")public String testShiroAnnotation(HttpSession session){session.setAttribute("key", "value12345");//寫入到SessionshiroService.testMethod();return "redirect:/list.jsp";}... }

    ShiroService

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

    24. SessionDao

    SessionDao可讓Session在數據庫中CRUD。

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

    配置示例

    <!-- Session ID生成器-- > <bean id="sessionIdGenerator"class="org.apache.shiro.session.mgt.eis.JavaUuidsessionIdGenerator"/><!-- Session DAO. 繼承EnterpriseCacheSessionDAO --> <bean id="sessionDAO" class="com.lun.shiro.realms.MySessionDao"><property name="activeSessionsCacheName" value="shiro-activeSessionCache"/><property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean><!-- 會話管理器--> <bean id="sessionManager" class="org.apache.shiro.session.mgt.Defaul tSessionManager"><property name="globalSessionTimeout" value="1800000"/><property name="deleteInvalidSessions" value="true"/><property name="sessionValidationSchedulerEnabled" value="true"/><property name="sessionDAO" ref="sessionDAO"/> </bean> <cache name="shiro-activeSessionCache"maxEntriesLocalHeap="10000"overflowToDisk="false"eternal="false"diskPersistent="false"timeToLiveSeconds="0"timeToIdleSeconds="0"statistics="true"/> create table sessions (id varchar(200),session varchar(2000),constraint pk_ sessions primary key(id) ) charset=utf8 ENGINE= InnoDB;

    Session Dao

    @Autowired private JdbcTemplate jdbcTemplate = nu1l;@Override protected Serializable doCreate(Session session){Serializable sessionId = generateSessionId(session);assignSessionId,(session, sessionId);String sql = "insert into sessions(id, session) values(?, ?)";|jdbcTemplate.update(sql,sessionId,serializableUtils. serialize (session);return session.getId(); }@Override protected Session doReadSession(Serializable sessionId){String sql = "select session from sessions where id=?";List<String> sessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId);if (sessionStrList.size() == 0)return null;return SerializableUtils.deserialize(sessionStrList.get(0)); }@Override protected void doUpdate (Session session) {if (session instanceof ValidatingSession&& ! ((ValidatingSession)session).isValid()) {return;String sql = "update sessions set session=? where id=?";jdbcTemplate.update(sql, SerializableUtils.serialize(session), session.getId()); }@Override protected void doDelete (Session session) {String sql = "delete from sessions where id=?";jdbcTemplate.update(sql, session.getId()); }

    SerializableUtils

    public static String serialize (Session session) {tryByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectoutputStream oos = new objectOutputStream(bos);oos.writeobject(session);return Base64.encodeTostring(bos.toByteArray());} catch(Exception e){throw new RuntimeException("serialize session error", e) ;} }public static Session deserialize (String sessionStr) {tryByteArrayInputStream bis = new ByteArrayInputStream();Base64.decode(sessionStr));ObjectInputstream ois = new objectInputstream(bis);return (Session)ois.readobject();} catch (Exception e) {throw new RuntimeException ("deserialize session error", e);} }

    25. 緩存

    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,查詢時會先查緩存,如果找不到才查數據庫。
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="cacheManager" ref="cacheManager"/>... </bean><bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"><property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> <!-- 配置文件 --> </bean>

    ehcache.xml

    <ehcache><diskStore path="java.io.tmpdir"/><!--Predefined caches. Add your cache configuration settings here.If you do not have a configuration for your cache a WARNING will be issued when theCacheManager startsThe following attributes are required for defaultCache:name - Sets the name of the cache. This is used to identify the cache. It must be unique.maxInMemory - Sets the maximum number of objects that will be created in memoryeternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the elementis never expired.timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only usedif the element is not eternal. Idle time is now - last accessed timetimeToLiveSeconds - Sets the time to live for an element before it expires. Is only usedif the element is not eternal. TTL is now - creation timeoverflowToDisk - Sets whether elements can overflow to disk when the in-memory cachehas reached the maxInMemory limit.--><cache name="authorizationCache"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="authenticationCache"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache><cache name="shiro-activeSessionCache"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true"></cache>...</ehcache>

    具體怎么使用的,視頻沒說,還需查閱更多資料

    26. 認證和記住我的區別

    Shiro 提供了記住我( RememberMe)的功能,比如訪問如淘寶等一些網站時,關閉了瀏覽器,下次再打開時還是能記住你是誰,下次訪問時無需再登錄即可訪問,基本流程如下:

  • 首先在登錄頁面選中 RememberMe 然后登錄成功;如果是瀏覽器登錄,一般會把 RememberMe 的Cookie 寫到客戶端并保存下來;
  • 關閉瀏覽器再重新打開,會發現瀏覽器還是記住你的;
  • 訪問一般的網頁服務器端還是知道你是誰,且能正常訪問;
  • 但是比如我們訪問淘寶時,如果要查看我的訂單或進行支付時,此時還是需要再進行身份認證的,以確保當前用戶還是你。
  • 27. 實現Remember me

    UsernamePasswordToken類中setRememberMe()

    ShiroHandler

    @Controller @RequestMapping("/shiro") public class ShiroHandler {...@RequestMapping("/login")public String login(@RequestParam("username") String username, @RequestParam("password") String password){Subject currentUser = SecurityUtils.getSubject();if (!currentUser.isAuthenticated()) {// 把用戶名和密碼封裝為 UsernamePasswordToken 對象UsernamePasswordToken token = new UsernamePasswordToken(username, password);// remembermetoken.setRememberMe(true);//設置remeberMetry {System.out.println("1. " + token.hashCode());...}}

    在applicationContext.xml中設置rememberMe的壽命

    ... <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">...<property name="rememberMeManager.cookie.maxAge" value="10"></property> </bean> ...

    總結

    以上是生活随笔為你收集整理的Shiro学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。