1 簡介
Shiro使用了與Servlet一樣的Filter接口進行擴展
Shiro攔截器的基礎類圖
1.1 NameableFilter
NameableFilter給Filter起個名字,如果沒有設置默認就是FilterName
當我們組裝攔截器鏈時會根據這個名字找到相應的攔截器實例
1.2 OncePerRequestFilter
用於防止多次執行Filter,也就是說一次請求只會走一次攔截器鏈
enabled
屬性:是否開啓該攔截器,默認enabled=true
表開啓
1.3 ShiroFilter
整個Shiro的入口點,用於攔截需要安全控制的請求進行處理
1.4 AdviceFilter
提供AOP風格的支持,類似於SpringMVC中的Interceptor
- preHandler:類AOP前置增強 在攔截器鏈執行之前執行;如果返回true則繼續攔截器鏈 否則中斷後續的攔截器鏈的執行直接返回 進行預處理(如基於表單的身份驗證、授權)
- postHandle:類AOP後置返回增強 在攔截器鏈執行完成後執行 進行後處理(如記錄執行時間之類的);
- afterCompletion:類AOP後置最終增強 不管有沒有異常都會執行 可以進行清理資源(如解除Subject與線程的綁定之類的)
1.5 PathMatchingFilter
提供基於Ant風格的請求路徑匹配功能及攔截器參數解析的功能,如roles[admin,user]
自動根據,
分割解析到一個路徑參數配置並綁定到相應的路徑
- pathsMatch
用於path與請求路徑進行匹配的方法,如果匹配返回true
- onPreHandle(待探討)
在preHandle中,當pathsMatch匹配一個路徑後,會調用onPreHandler方法並將路徑綁定參數配置傳給mappedValue;然後可以在這個方法中進行一些驗證(如角色授權),如果驗證失敗可以返回false中斷流程;默認返回true;也就是說子類可以只實現onPreHandle即可,無須實現preHandle。如果沒有path與請求路徑匹配,默認是通過的(即preHandle返回true)
1.6 AccessControlFilter
提供訪問控制的基礎功能;比如是否允許訪問/當訪問拒絕時如何處理等
- isAccessAllowed
表是否允許訪問;mappedValue就是[urls]配置中攔截器參數部分,如果允許訪問返回true,否則false
- onAccessDenied
表當訪問拒絕時是否已經處理 如果返回true表示需要繼續處理 如果返回false表示該攔截器實例已經處理了,直接返回即可
onPreHandle會自動調用這兩個方法決定是否繼續處理
AccessControlFilter還提供瞭如下方法用於處理如登錄成功後/重定向到上一個請求
void setLoginUrl(String loginUrl) //身份驗證時使用,默認/login.jsp String getLoginUrl() Subject getSubject(ServletRequest request, ServletResponse response) //獲取Subject實例 boolean isLoginRequest(ServletRequest request, ServletResponse response)//當前請求是否是登錄請求 void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException //將當前請求保存起來並重定向到登錄頁面 void saveRequest(ServletRequest request) //將請求保存起來,如登錄成功後再重定向回該請求 void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登錄頁面
比如基於表單的身份驗證就需要使用這些功能
到此基本的攔截器完結 若我們想進行訪問控制就可以繼承AccessControlFilter 若我們要添加一些通用數據我們可以直接繼承PathMatchingFilter
2 過濾器鏈
2.1 簡介
Shiro對Servlet容器的FilterChain進行了代理,即ShiroFilter在繼續Servlet容器的Filter鏈的執行之前,通過ProxiedFilterChain
對Servlet容器的FilterChain進行了代理
即先走Shiro自己的Filter體系,然後纔會委託給Servlet容器的FilterChain進行Servlet容器級別的Filter鏈執行
Shiro的ProxiedFilterChain
執行流程
- 先執行Shiro自己的Filter鏈
- 再執行Servlet容器的Filter鏈(即原始的Filter)
而ProxiedFilterChain是通過FilterChainResolver根據配置文件中[urls]部分是否與請求的URL是否匹配解析得到的
即傳入原始的chain得到一個代理的chain
Shiro內部提供了一個路徑匹配的FilterChainResolver實現:PathMatchingFilterChainResolver 其根據[urls]中配置的url模式(默認Ant風格) 即根據過濾器鏈和請求的url是否匹配來解析得到配置的過濾器鏈 而PathMatchingFilterChainResolver內部通過FilterChainManager
維護過濾器鏈 比如DefaultFilterChainManager
維護着url模式與過濾器鏈的關係 因此我們可以通過FilterChainManager進行動態動態增加url模式與過濾器鏈的關係
DefaultFilterChainManager會默認添加org.apache.shiro.web.filter.mgt.DefaultFilter
中聲明的過濾器
2.2 註冊自定義攔截器
IniSecurityManagerFactory/WebIniSecurityManagerFactory在啓動時會自動掃描ini配置文件中的[filters]/[main]部分並註冊這些攔截器到DefaultFilterChainManager 且創建相應的url模式與其攔截器關係鏈
如果想自定義FilterChainResolver,可以通過實現WebEnvironment接口完成
public class MyIniWebEnvironment extends IniWebEnvironment { @Override protected FilterChainResolver createFilterChainResolver() { //在此處擴展自己的FilterChainResolver return super.createFilterChainResolver(); } }
FilterChain之間的關係。如果想動態實現url-攔截器的註冊,就可以通過實現此處的FilterChainResolver來完成,比如:
//1、創建FilterChainResolver PathMatchingFilterChainResolver filterChainResolver = new PathMatchingFilterChainResolver(); //2、創建FilterChainManager DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager(); //3、註冊Filter for(DefaultFilter filter : DefaultFilter.values()) { filterChainManager.addFilter( filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass())); } //4、註冊URL-Filter的映射關係 filterChainManager.addToChain("/login.jsp", "authc"); filterChainManager.addToChain("/unauthorized.jsp", "anon"); filterChainManager.addToChain("/**", "authc"); filterChainManager.addToChain("/**", "roles", "admin"); //5、設置Filter的屬性 FormAuthenticationFilter authcFilter = (FormAuthenticationFilter)filterChainManager.getFilter("authc"); authcFilter.setLoginUrl("/login.jsp"); RolesAuthorizationFilter rolesFilter = (RolesAuthorizationFilter)filterChainManager.getFilter("roles"); rolesFilter.setUnauthorizedUrl("/unauthorized.jsp"); filterChainResolver.setFilterChainManager(filterChainManager); return filterChainResolver;
此處自己去實現註冊filter,及url模式與filter之間的映射關係 可以通過定製FilterChainResolver或FilterChainManager來完成諸如動態URL匹配的實現
然後再web.xml中進行如下配置Environment
<context-param> <param-name>shiroEnvironmentClass</param-name> <param-value>com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment</param-value> </context-param>
2.3 自定義過濾器
通過自定義自己的過濾器可以擴展一些功能,諸如動態url-角色/權限訪問控制的實現、根據Subject身份信息獲取用戶信息綁定到Request(即設置通用數據)、驗證碼驗證、在線用戶信息的保存等等,因爲其本質就是一個Filter;所以Filter能做的它就能做
2.3.1 擴展OncePerRequestFilter
OncePerRequestFilter保證一次請求只調用一次doFilterInternal,即如內部的forward不會再多執行一次doFilterInternal:
public class MyOncePerRequestFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("=========once per request filter"); chain.doFilter(request, response); } }
然後再shiro.ini配置文件中
[main] myFilter1=com.sss.web.filter.MyOncePerRequestFilter #[filters] #myFilter1=com.sss.web.filter.MyOncePerRequestFilter [urls] /**=myFilter1