shiro源碼篇 - 疑問解答與系列總結,你值得擁有

来源:https://www.cnblogs.com/youzhibing/archive/2018/12/24/10154369.html
-Advertisement-
Play Games

前言 開心一刻 小明的朋友骨折了,小明去他家裡看他。他老婆很細心的為他換藥,敷藥,然後出去買菜。小明滿臉羡慕地說:你特麽真幸福啊,你老婆對你那麼好!朋友哭得稀里嘩啦的說:兄弟你別說了,我幸福個錘子,就是她把我打骨折的。 揣摩下此刻男人的內心 路漫漫其修遠兮,吾將上下而求索! github:https ...


前言

  開心一刻

    小明的朋友骨折了,小明去他家裡看他。他老婆很細心的為他換藥,敷藥,然後出去買菜。小明滿臉羡慕地說:你特麽真幸福啊,你老婆對你那麼好!朋友哭得稀里嘩啦的說:兄弟你別說了,我幸福個錘子,就是她把我打骨折的。

揣摩下此刻男人的內心

  路漫漫其修遠兮,吾將上下而求索!

  github:https://github.com/youzhibing

  碼雲(gitee):https://gitee.com/youzhibing

前情回顧

  上篇中主要講了兩點認證與授權,認證主要FormAuthenticationFilter和AnonymousFilter兩個filter來控制,shiro對所有請求都會先生成ProxiedFilterChain,請求會經過ProxiedFilterChain,先執行shiro的filter鏈,再執行剩下的servlet Filter鏈,最後來到我們的Controller。

  認證過程是通過filter控制實現的,我們所有的請求由shiro中3個Filter:LogoutFilter、AnonymousFilter、FormAuthenticationFilter分攤了,LogoutFilter負責/logout,AnonymousFilter負責/login和靜態資源,FormAuthenticationFilter則負責剩下的(/**),三個filter只會有一個生效(註意filter的配置順序);當FormAuthenticationFilter生效的時候會進行登錄認證,認證過程:先從緩存獲取authenticationInfo,沒有則通過realm從資料庫獲取並放入緩存,然後將頁面輸入的用戶信息(UsernamePasswordToken)與authenticationInfo進行匹配驗證,認證通過會將subject中的authenticated設置成true,表示當前subject已經被認證過了。關於認證緩存,個人不建議開啟,因為當修改用戶信息後,不好處理緩存中的authenticationInfo,另外認證頻率本來就不高,緩存的意義不大。

  一般情況下授權是通過註解方式實現的,註解配合aop會在我們的業務方法前織入前置許可權檢查處理,檢查過程與認證過程類似:從緩存中獲取authorizationInfo,沒有則通過realm從資料庫獲取,然後放入緩存,然後將authorizationInfo與@RequiresPermissions("xxx")中的xxx來進行匹配,完成許可權檢查,檢查通過則進入我們的目標方法,不通過則拋出異常。關於許可權緩存,個人建議開啟,因為許可權的驗證還是挺頻繁的,如果不開啟緩存,那麼會給資料庫造成一定的壓力。

遺留問題解答

  上篇遺留問題:session過期後,我們再請求,shiro是如何處理並跳轉到登錄頁的?回答這個問題之前,我們先看看另外一個問題:

上篇博文中講到了登錄認證成功後會將subject的authenticated設置成true,表示當前subject已經被認證過了,但是只是當前subject;
我們可以將subject看成是request,每次請求來的時候都會將request/response對封裝成subject,AbstractShiroFilter的方法doFilterInternal中有這樣一個調用

final Subject subject = createSubject(request, response);

我們來看看createSubject的方法描述:
    Creates a  WebSubject instance to associate with the incoming request/response pair which will be used throughout the request/response execution.
    創建一個關聯request/response對的WebSubject實例,用於後續request/response的執行

  也就是目前我們還只是看到了當前請求有認證狀態,當前會話還沒有看到認證狀態;撇開shiro,如果是我們自己實現,我們會怎麼實現,肯定會在subject的authenticated設置成true的時候也將認證狀態也設置在session中,至於是存儲在自定義session的某個標誌欄位(類似subject的authenticated)中,還是存儲在session的attributes中(setAttribute(Object key, Object value)進行設置),看我們的需求和個人喜好。

  歸納下這個問題:shiro是如何保存當前會話認證狀態的,是上述中的某種實現方式,還是shiro有另外的實現方式

  shiro是如何保存會話認證狀態的

    每次請求都會生成新的subject,如果我們把認證狀態只放到subject中,那麼每次請求都需要進行認證,這顯然是不合理的,我們需要將認證狀態保存到會話(session)中,那麼整個會話期間只需要認證一次即可。那麼shiro是怎麼實現的了,我們來看看源碼,從我們Controller的doLogin方法開始

    如果我們繼續跟進s.setAttribute(attributeKey, value),會發現認證狀態最終存放到了SimpleSession的attributes屬性中,

private transient Map<Object, Object> attributes;

    也就是說認證狀態會存在session的attributes中,正是我們上面說的方式之一,shiro沒有用它特有的方式,最終在session中的存在形式如下圖

    看過上篇博客的朋友應該會有印象:FormAuthenticationFilter的isAccessAllowed方法(從AuthenticatingFilter繼承)中第一個認證的是subject的authenticated

    為什麼獲取subject的authenticated,而不是直接獲取session的認證狀態,我還沒弄清楚為什麼,難道是為了組件的分工明確? 既然shiro這麼做了肯定有它的道理,我們先不糾結這個(知道的朋友可以評論區提示下)。我們知道登錄成功後,subject的authenticated會被賦值成true,但是登錄成功後的其他請求,比如:http://localhost:8080/own/index,subject的authenticated是什麼時候在哪被賦值成true的呢?我們已經知道session中有認證狀態,那麼肯定是某個時候在某個地方將session中的認證狀態賦值給了subject,具體是怎麼樣我們來跟一下源碼,還記得shiro的入口filter:SpringShiroFilter,我們從SpringShiroFilter的doFilterInternal(從AbstractShiroFilter繼承)方法開始

    可以看到,在創建subject的時候,會將session中的認證狀態賦值給subject的authenticated。

    小結下:登錄時,登錄成功會將認證狀態(成功)存儲到session的attributes中,之後的每一次請求,在創建subject的時候,都會將session中的認證狀態賦值給subject的authenticated,那麼FormAuthenticationFilter在認證的時候會直接返回true,繼續走servlet filter鏈,最終來到我們的Controller。

    至此,該問題就明瞭了,會話認證狀態還是保存在session中,只是中間處理的時候會將session中的認證狀態賦值給subject,由subject傳遞給FormAuthenticationFilter認證狀態。

  session過期後,我們再請求,shiro是如何處理並跳轉到登錄頁的

    如果我們明白了上個問題,那麼這個問題就很好理解了。如果session過期,那麼通過sessionDAO獲取的session為null,subject的authenticated就會被賦值成false,那麼在FormAuthenticationFilter中認證不通過,則會重定向到/login,讓用戶重新進行登錄認證。事實是這樣嗎,我們來跟下源碼(假設此時請求是:http://localhost:8080/own/index)

    可以看到,請求進過SpringShiroFilter時,subject中authenticated被設置成false,然後生成ProxiedFilterChain

    請求會來到FormAuthenticationFilter,認證不通過,重定向到/login,並返回false,表示filter鏈不用繼續往下走了(具體可查看上篇博文)。

    強調下:很多對session的操作,都會同步到緩存(或持久層),包括session刷新(touch())、設置session屬性(setAttribute()等等,具體可以看AbstractNativeSessionManager,很多session操作中都會調用onChange方法

protected void onChange(Session session) {
    sessionDAO.update(session);
}

shiro源碼系列

  系列地址

  shiro源碼篇 - shiro的session創建,你值得擁有

    SessionManager負責session的操作,包括創建、維護、刪除、失效、驗證等;

    AbstractNativeSessionManager的start是創建session的入口;

    SimpleSession是shiro完完全全的自己實現,是shiro對session的一種拓展。但SimpleSession不對外暴露,我們一般操作的是SimpleSession的代理:DelegatingSession,或者是DelegatingSession的代理:StoppingAwareProxiedSession;對session的操作,會通過一層層代理,來到DelegatingSession,DelegatingSession將session的操作轉交給sessionMananger,sessionManager通過一些校驗後,最後轉交給SimpleSession處理。

  shiro源碼篇 - shiro的session的查詢、刷新、過期與刪除,你值得擁有

    一般操作的session是session的代理,代理將session操作委托給sessionManager,sesionManager校驗之後再轉交給SimpleSession;

    session過期定時任務預設60分鐘執行一次,所session已過期或不合法,則拋出對應的異常,上層通過捕獲異常從sessionDAO中刪除session;

    不只定時任務做session的校驗,session的基本操作都在sessionManager中有做session的校驗,例如touch、setAttribute等,具體可以查看AbstractNativeSessionManager,對session的操作都是通過AbstractNativeSessionManager處理後轉交給SimpleSession。

  shiro源碼篇 - shiro的session共用,你值得擁有

    session共用實現的原理其實都是一樣的,都是filter + HttpServletRequestWrapper,只是實現細節會有所區別;

    shiro的session共用其實是比較簡單的,重寫CacheManager,將其操作指向我們的redis,然後定製CachingSessionDAO實現session緩存操作和session持久化;

    如果session需要持久化,推薦自定義sessionDAO繼承EnterpriseCacheSessionDAO,如果只是緩存,則推薦自定義sessionDAO繼承CachingSessionDAO。

  shiro源碼篇 - shiro的filter,你值得擁有

    SpringShiroFilter註冊到spring容器,會被包裝成FilterRegistrationBean,通過FilterRegistrationBean註冊到servlet容器;SpringShiroFilter相當於是整個shiro的入口;

    SpringShiroFilter會創建ProxiedFilterChain,代理servlet FilterChain,讓請求先走shiro的filter鏈,讓後再走servlet FilterChain。

  shiro源碼篇 - shiro認證與授權,你值得擁有

    認證通過Filter實現,anon表示匿名訪問,不需要認證,一般就是針對游客可以訪問的資源,而authc則表示需要登錄認證;

    我們所有的請求一般由shiro中3個Filter:LogoutFilter、AnonymousFilter、FormAuthenticationFilter分攤了,LogoutFilter負責/logout,AnonymousFilter負責/login和靜態資源,FormAuthenticationFilter則負責剩下的(/**);

    認證由FormAuthenticationFilter實現,未登錄的請求會由它重定向到/login;認證過程是將界面輸入的信息(UsernamePasswordToken)與緩存(或資料庫)中的authenticationInfo進行匹對驗證;認證信息不建議緩存;

    授權由註解方式,配合aop實現目標方法前的增強織入;認證過程是將緩存(或資料庫)中的authorizationInfo與@RequiresPermissions("xxx")中的xxx進行匹配校驗;認證信息建議緩存起來。

參考

  《跟我學shiro》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript 系列博客(五) 前言 本篇博客學習 js 選擇器來控制 css 和 html、使用事件(鉤子函數)來處理事件完成後完成指定功能以及js 事件控制頁面內容。 js 選擇器 在學習 js 選擇器前需要瞭解幾個概念。 節點(一):在文檔(document)中出現的所有內容都是 doc ...
  • 1.同源問題解決 首先,在同一個域下搭建網路功能變數名稱訪問,需要nginx軟體,下載之後修改部分配置 然後再終端下cmd nginx.exe命令,或者打開nginx.exe文件,會運行nginx一閃而過,在後臺運行而且一次是打開兩個的,可以在任務管理器控制結束進程, 接下來,你就可以打開8080介面給同域 ...
  • 本文是 "Rxjs 響應式編程 第一章:響應式" 這篇文章的學習筆記。 示例代碼地址: "【示例代碼】" 更多文章: "【《大史住在大前端》博文集目錄】" [TOC] 一. 劃重點 三句非常重要的話: 從理念上來理解,Rx模式引入了一種新的 “一切皆流” 的編程範式 從設計模式的角度來看, 是 發佈 ...
  • 國內的設計師大都喜歡用px,而國外的網站大都喜歡用em和rem,那麼三者有什麼區別,又各自有什麼優劣呢? PX特點 1. IE無法調整那些使用px作為單位的字體大小; 2. 國外的大部分網站能夠調整的原因在於其使用了em或rem作為字體單位; 3. Firefox能夠調整px和em,rem,但是96 ...
  • react本身能夠完成動態數據的監聽和更新,如果不是必要可以不適用redux。 安裝redux: cnpm install redux --save,或者yarn add redux。 一、react基本用法 redux是獨立的用於狀態管理的第三方包,它建立狀態機來對單項數據進行管理。 上圖是個人粗 ...
  • 裝飾器模式允許向現有對象中添加新功能,同時又不改變其結構。 介紹 裝飾器模式屬於結構型模式,主要功能是能夠動態地為一個對象添加額外功能。在保證現有功能的基礎上,再添加新功能,可聯想到 WPF 中的附件屬性。 類圖描述 由上圖可知,我們定義了一個基礎介面 IShape 用於約定對象的基礎行為。然後通過 ...
  • 在實際應用中,集群環境里共用一些數據是不可避免的。我的意思是有些數據可以在任何節點進行共用同步讀寫,困難的是如何解決更改衝突問題。本來可以通過分散式資料庫來實現這樣的功能,但使用和維護成本又過高,不值得。分散式數據類型distributed-data (ddata)正是為解決這樣的困局而設計的。ak ...
  • 在上一篇博客中,我“蜻蜓點水”般的介紹了下Java記憶體模型,在這一篇博客,我將帶著大家看下Synchronized關鍵字的那些事,其實把Synchronized關鍵字放到上一篇博客中去介紹,也是符合 “Java記憶體模型”這個標題的,因為Synchronized關鍵字和Java記憶體模型有著密不可分的關 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...