Shiro 實戰(四) - 過濾器機制1 簡介2 過濾器鏈

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 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章