一 shiro自帶的filter:下面主要敘述順序是 NameableFilter-》OncePerRequestFilter-》AdviceFilter-》PathMatchingFilter-》AuthenticationFilter(AuthenticatingFilter)-》FromAuthenticationFilter

①NameableFilter有一個name屬性,定義每一個filter的名字。在FilterChainManager中會調用配置文件中的配置屬性名字來為每一個filter命名以及為默認的filter命名,如authc。

②OncePerRequestFilter保證客戶端請求后該filter的doFilter只會執行一次。

可以看出doFilter的實質內容是在doFilterInternal方法中完成的。所以實質上是保證每一個filter的 doFilterInternal只會被執行一次,例如在配置中 配置路徑 /user/** = authc,authc.則只會執行authc中的doFilterInternal一次。doFilterInternal非常重要,在shiro整個filter體系中的核心方法及實質入口。另外,shiro是通過在request中設置一個該filter特定的屬性值來保證該filter只會執行一次的。
其次可以看到有一個enabled屬性,表示是否啟用這個filter,默認是true,可以設置成false,則會跳過這個filter的doFilterInternal方法而去執行filter鏈中其他filter。
③AdviceFilter中主要是對doFilterInternal做了更細致的切分


這有點像springmvc中的Interceptor,doFilterInternal會先調用preHandle做一些前置判斷,如果返回false則filter鏈不繼續往下執行,postHandle在目標方法(即客戶端請求的接口)正常(未拋出異常)執行后完成一些操作,默認不做任何操作。在finally中的cleanup方法中會調用afterCompletion方法,不管目標方法是否出現異常都會繼續操作。默認也是空。 AdviceFilter總體是對OncePerRequestFilter中的doFilterInternal進一步細化控制。
④PathMatchingFilter主要是對preHandle做進一步細化控制,該filter為抽象類,其他路徑直接通過:preHandle中,若請求的路徑非該filter中配置的攔截路徑,則直接返回true進行下一個filter。若包含在此filter路徑中,則會在isFilterChainContinued做一些控制,該方法中會調用onPreHandle方法,所以子類可以在onPreHandle中編寫filter控制流程代碼(返回true或false)。


⑤AccessControlFilter中的對onPreHandle方法做了進一步細化,isAccessAllowed方法和onAccessDenied方法達到控制效果。這兩個方法都是抽象方法,由子類去實現。到這一層應該明白。isAccessAllowed和onAccessDenied方法會影響到onPreHandle方法,而onPreHandle方法會影響到preHandle方法,而preHandle方法會達到控制filter鏈是否執行下去的效果。所以如果正在執行的filter中isAccessAllowed和onAccessDenied都返回false,則整個filter控制鏈都將結束,不會到達目標方法(客戶端請求的接口),而是直接跳轉到某個頁面(由filter定義的,將會在authc中看到)。


⑥AuthenticationFilter和AuthenticatingFilter認證的filter,在抽象類中AuthenticatingFilter實現了isAccessAllowed方法,該方法是用來判斷用戶是否已登錄,若未登錄再判斷是否請求的是登錄地址,是登錄地址則放行,否則返回false終止filter鏈。



另外可以看到提供了executeLogin方法實現用戶登錄的,還定義了onLoginSuccess和onLoginFailure方法,在登錄成功或者失敗時做一些操作。登錄將在下面詳細說明。
⑦FormAuthenticationFiltershiro提供的登錄的filter,如果用戶未登錄,即AuthenticatingFilter中的isAccessAllowed判斷了用戶未登錄,則會調用onAccessDenied方法做用戶登錄操作。若用戶請求的不是登錄地址,則跳轉到登錄地址,並且返回false直接終止filter鏈。若用戶請求的是登錄地址,若果是post請求則進行登錄操作,由AuthenticatingFilter中提供的executeLogin方法執行。否則直接通過繼續執行filter鏈,並最終跳轉到登錄頁面(因為用戶請求的就是登錄地址,若不是登錄地址也會重定向到登錄地址).

AuthenticatingFilter中的executeLogin

若登錄成功返回false(FormAuthenticationFiltershiro的onLoginSuccess默認false),則表示終止filter鏈,直接重定向到成功頁面,甚至不到達目標方法直接返回了。若登錄失敗,直接返回true(onLoginFailure返回false),繼續執行filter鏈並最終跳轉到登錄頁面,該方法還會設置一些登錄失敗提示 shiroLoginFailure,在目標方法中可以根據這個錯誤提示制定客戶端更加友好的錯誤提示。


二 自定義filter
一般自定義filter可以繼承三種:
①OncePerRequestFilter只需實現doFilterInternal方法即可,在這里面實現filter的功能。切記在該方法中最后調用filterChain.doFilter(request, response),允許filter鏈繼續執行下去。可以在這個自定義filter中覆蓋isEnable達到控制該filter是否需要被執行(實質是doFilterInternal方法)以達到動態控制的效果,一般不建議直接繼承這個類;
②AdviceFilter 中提供三個方法preHandle postHandle afterCompletion:若需要在目標方法執行前后都做一些判斷的話應該繼承這個類覆蓋preHandle 和postHandle 。
③PathMatchingFilter中preHandle實質會判斷onPreHandle來決定是否繼續往下執行。所以只需覆蓋onPreHandle方法即可。
④AccessControlFilter:最常用的,該filter中onPreHandle調用isAccessAllowed和onAccessDenied決定是否繼續執行。一般繼承該filter,isAccessAllowed決定是否繼續執行。onAccessDenied做后續的操作,如重定向到另外一個地址、添加一些信息到request域等等。
④若要自定義登錄filter,一般是由於前端傳過來的需求所定義的token與shiro默認提供token的不符,可以繼承AuthenticatingFilter ,在這里面實現createToken來創建自定義token。另外需要自定義憑證匹配器credentialsMatcher。重寫public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)即可。realm也需要自定義以返回自定義的token。
三shiro登錄
由前面filter鏈可以看出登錄已經很清晰了。shiro提供的FormAuthenticationFilter認證過濾器,繼承了AuthenticatingFilter ,若已登錄則isAccessAllowed直接通過,否則在 onAccessDenied中判斷是否是登錄請求,若是請求登錄頁面,直接通過,若是post提交登錄信息則會進行登錄操作。否則直接跳轉到登錄頁面。登錄是由shiro的securityManager完成的,securityManager從Realm獲取用戶的真實身份,從FormAuthenticationFilter的createToken獲取用戶提交的token,credentialsMatcher完成是否匹配成功操作。
