shiro学习(19): 拦截器
1 攔截器介紹
?? Shiro使用了與Servlet一樣的Filter接口進(jìn)行擴(kuò)展;所以如果對(duì)Filter不熟悉可以參考《Servlet3.1規(guī)范》http://www.iteye.com/blogs/subjects/Servlet-3-1了解Filter的工作原理。首先下圖是Shiro攔截器的基礎(chǔ)類(lèi)圖:
1、NameableFilter
??? NameableFilter給Filter起個(gè)名字,如果沒(méi)有設(shè)置默認(rèn)就是FilterName;還記得之前的如authc嗎?當(dāng)我們組裝攔截器鏈時(shí)會(huì)根據(jù)這個(gè)名字找到相應(yīng)的攔截器實(shí)例;
2、OncePerRequestFilter
??? OncePerRequestFilter用于防止多次執(zhí)行Filter的;也就是說(shuō)一次請(qǐng)求只會(huì)走一次攔截器鏈;另外提供enabled屬性,表示是否開(kāi)啟該攔截器實(shí)例,默認(rèn)enabled=true表示開(kāi)啟,如果不想讓某個(gè)攔截器工作,可以設(shè)置為false即可。
3、ShiroFilter
??? ShiroFilter是整個(gè)Shiro的入口點(diǎn),用于攔截需要安全控制的請(qǐng)求進(jìn)行處理,這個(gè)之前已經(jīng)用過(guò)了。
4、AdviceFilter
??? AdviceFilter提供了AOP風(fēng)格的支持,類(lèi)似于SpringMVC中的Interceptor
?
[java] view plain copy print?
??? preHandler:類(lèi)似于AOP中的前置增強(qiáng);在攔截器鏈執(zhí)行之前執(zhí)行;如果返回true則繼續(xù)攔截器鏈;否則中斷后續(xù)的攔截器鏈的執(zhí)行直接返回;進(jìn)行預(yù)處理(如基于表單的身份驗(yàn)證、授權(quán))
??? postHandle:類(lèi)似于AOP中的后置返回增強(qiáng);在攔截器鏈執(zhí)行完成后執(zhí)行;進(jìn)行后處理(如記錄執(zhí)行時(shí)間之類(lèi)的);
??? afterCompletion:類(lèi)似于AOP中的后置最終增強(qiáng);即不管有沒(méi)有異常都會(huì)執(zhí)行;可以進(jìn)行清理資源(如接觸Subject與線(xiàn)程的綁定之類(lèi)的);
?
5、PathMatchingFilter
??? PathMatchingFilter提供了基于Ant風(fēng)格的請(qǐng)求路徑匹配功能及攔截器參數(shù)解析的功能,如“roles[admin,user]”自動(dòng)根據(jù)“,”分割解析到一個(gè)路徑參數(shù)配置并綁定到相應(yīng)的路徑:
[java] view plain copy print?
??? pathsMatch:該方法用于path與請(qǐng)求路徑進(jìn)行匹配的方法;如果匹配返回true;
??? onPreHandle:在preHandle中,當(dāng)pathsMatch匹配一個(gè)路徑后,會(huì)調(diào)用opPreHandler方法并將路徑綁定參數(shù)配置傳給mappedValue;然后可以在這個(gè)方法中進(jìn)行一些驗(yàn)證(如角色授權(quán)),如果驗(yàn)證失敗可以返回false中斷流程;默認(rèn)返回true;也就是說(shuō)子類(lèi)可以只實(shí)現(xiàn)onPreHandle即可,無(wú)須實(shí)現(xiàn)preHandle。如果沒(méi)有path與請(qǐng)求路徑匹配,默認(rèn)是通過(guò)的(即preHandle返回true)。
6、AccessControlFilter
??? AccessControlFilter提供了訪(fǎng)問(wèn)控制的基礎(chǔ)功能;比如是否允許訪(fǎng)問(wèn)/當(dāng)訪(fǎng)問(wèn)拒絕時(shí)如何處理等:
?
[java] view plain copy print?
??? isAccessAllowed:表示是否允許訪(fǎng)問(wèn);mappedValue就是[urls]配置中攔截器參數(shù)部分,如果允許訪(fǎng)問(wèn)返回true,否則false;
??? onAccessDenied:表示當(dāng)訪(fǎng)問(wèn)拒絕時(shí)是否已經(jīng)處理了;如果返回true表示需要繼續(xù)處理;如果返回false表示該攔截器實(shí)例已經(jīng)處理了,將直接返回即可。
?
onPreHandle會(huì)自動(dòng)調(diào)用這兩個(gè)方法決定是否繼續(xù)處理:
[java] view plain copy print?
另外AccessControlFilter還提供了如下方法用于處理如登錄成功后/重定向到上一個(gè)請(qǐng)求:
[java] view plain copy print?
??? 比如基于表單的身份驗(yàn)證就需要使用這些功能。
??? 到此基本的攔截器就完事了,如果我們想進(jìn)行訪(fǎng)問(wèn)訪(fǎng)問(wèn)的控制就可以繼承AccessControlFilter;如果我們要添加一些通用數(shù)據(jù)我們可以直接繼承PathMatchingFilter。
?
2 攔截器鏈
??? Shiro對(duì)Servlet容器的FilterChain進(jìn)行了代理,即ShiroFilter在繼續(xù)Servlet容器的Filter鏈的執(zhí)行之前,通過(guò)ProxiedFilterChain對(duì)Servlet容器的FilterChain進(jìn)行了代理;即先走Shiro自己的Filter體系,然后才會(huì)委托給Servlet容器的FilterChain進(jìn)行Servlet容器級(jí)別的Filter鏈執(zhí)行;
??? Shiro的ProxiedFilterChain執(zhí)行流程:1、先執(zhí)行Shiro自己的Filter鏈;2、再執(zhí)行Servlet容器的Filter鏈(即原始的Filter)。
??? 而ProxiedFilterChain是通過(guò)FilterChainResolver根據(jù)配置文件中[urls]部分是否與請(qǐng)求的URL是否匹配解析得到的。
[java] view plain copy print?
??? 即傳入原始的chain得到一個(gè)代理的chain。
??? Shiro內(nèi)部提供了一個(gè)路徑匹配的FilterChainResolver實(shí)現(xiàn):PathMatchingFilterChainResolver,其根據(jù)[urls]中配置的url模式(默認(rèn)Ant風(fēng)格)=攔截器鏈和請(qǐng)求的url是否匹配來(lái)解析得到配置的攔截器鏈的;而PathMatchingFilterChainResolver內(nèi)部通過(guò)FilterChainManager維護(hù)著攔截器鏈,比如DefaultFilterChainManager實(shí)現(xiàn)維護(hù)著url模式與攔截器鏈的關(guān)系。因此我們可以通過(guò)FilterChainManager進(jìn)行動(dòng)態(tài)動(dòng)態(tài)增加url模式與攔截器鏈的關(guān)系。
??? DefaultFilterChainManager會(huì)默認(rèn)添加org.apache.shiro.web.filter.mgt.DefaultFilter中聲明的攔截器:
[java] view plain copy print?
??? 如果要注冊(cè)自定義攔截器,IniSecurityManagerFactory/WebIniSecurityManagerFactory在啟動(dòng)時(shí)會(huì)自動(dòng)掃描ini配置文件中的[filters]/[main]部分并注冊(cè)這些攔截器到DefaultFilterChainManager;且創(chuàng)建相應(yīng)的url模式與其攔截器關(guān)系鏈。如果使用Spring后續(xù)章節(jié)會(huì)介紹如果注冊(cè)自定義攔截器。
??? 如果想自定義FilterChainResolver,可以通過(guò)實(shí)現(xiàn)WebEnvironment接口完成:
[java] view plain copy print?
??? FilterChain之間的關(guān)系。如果想動(dòng)態(tài)實(shí)現(xiàn)url-攔截器的注冊(cè),就可以通過(guò)實(shí)現(xiàn)此處的FilterChainResolver來(lái)完成,比如:
[java] view plain copy print?
??? 此處自己去實(shí)現(xiàn)注冊(cè)filter,及url模式與filter之間的映射關(guān)系。可以通過(guò)定制FilterChainResolver或FilterChainManager來(lái)完成諸如動(dòng)態(tài)URL匹配的實(shí)現(xiàn)。
?
???? 然后再web.xml中進(jìn)行如下配置Environment:??
[html] view plain copy print?
3 自定義攔截器
??? 通過(guò)自定義自己的攔截器可以擴(kuò)展一些功能,諸如動(dòng)態(tài)url-角色/權(quán)限訪(fǎng)問(wèn)控制的實(shí)現(xiàn)、根據(jù)Subject身份信息獲取用戶(hù)信息綁定到Request(即設(shè)置通用數(shù)據(jù))、驗(yàn)證碼驗(yàn)證、在線(xiàn)用戶(hù)信息的保存等等,因?yàn)槠浔举|(zhì)就是一個(gè)Filter;所以Filter能做的它就能做。
??? 對(duì)于Filter的介紹請(qǐng)參考《Servlet規(guī)范》中的Filter部分:http://www.iteye.com/blogs/subjects/Servlet-3-1。
1、擴(kuò)展OncePerRequestFilter
??? OncePerRequestFilter保證一次請(qǐng)求只調(diào)用一次doFilterInternal,即如內(nèi)部的forward不會(huì)再多執(zhí)行一次doFilterInternal:
[java] view plain copy print?
然后再shiro.ini配置文件中:
[java] view plain copy print?
??? Filter可以在[main]或[filters]部分注冊(cè),然后在[urls]部分配置url與filter的映射關(guān)系即可。
2、擴(kuò)展AdviceFilter
??? AdviceFilter提供了AOP的功能,其實(shí)現(xiàn)和SpringMVC中的Interceptor思想一樣:具體可參考我的SpringMVC教程中的處理器攔截器部分:http://www.iteye.com/blogs/subjects/kaitao-springmvc
[java] view plain copy print?
??? preHandle:進(jìn)行請(qǐng)求的預(yù)處理,然后根據(jù)返回值決定是否繼續(xù)處理(true:繼續(xù)過(guò)濾器鏈);可以通過(guò)它實(shí)現(xiàn)權(quán)限控制;
??? postHandle:執(zhí)行完攔截器鏈之后正常返回后執(zhí)行;
??? afterCompletion:不管最后有沒(méi)有異常,afterCompletion都會(huì)執(zhí)行,完成如清理資源功能。
?然后在shiro.ini中進(jìn)行如下配置:
?
[java] view plain copy print?
?
??? 該過(guò)濾器的具體使用可參考我的SpringMVC教程中的處理器攔截器部分。
?3、PathMatchingFilter
??? PathMatchingFilter繼承了AdviceFilter,提供了url模式過(guò)濾的功能,如果需要對(duì)指定的請(qǐng)求進(jìn)行處理,可以擴(kuò)展PathMatchingFilter:
?
[java] view plain copy print?
??? preHandle:會(huì)進(jìn)行url模式與請(qǐng)求url進(jìn)行匹配,如果匹配會(huì)調(diào)用onPreHandle;如果沒(méi)有配置url模式/沒(méi)有url模式匹配,默認(rèn)直接返回true;
??? onPreHandle:如果url模式與請(qǐng)求url匹配,那么會(huì)執(zhí)行onPreHandle,并把該攔截器配置的參數(shù)傳入。默認(rèn)什么不處理直接返回true。
然后在shiro.ini中進(jìn)行如下配置:
[html] view plain copy print?
???? /**就是注冊(cè)給PathMatchingFilter的url模式,config就是攔截器的配置參數(shù),多個(gè)之間逗號(hào)分隔,onPreHandle使用mappedValue接收參數(shù)值。
4、擴(kuò)展AccessControlFilter
??? AccessControlFilter繼承了PathMatchingFilter,并擴(kuò)展了了兩個(gè)方法:
[java] view plain copy print?
??? isAccessAllowed:即是否允許訪(fǎng)問(wèn),返回true表示允許;
??? onAccessDenied:表示訪(fǎng)問(wèn)拒絕時(shí)是否自己處理,如果返回true表示自己不處理且繼續(xù)攔截器鏈執(zhí)行,返回false表示自己已經(jīng)處理了(比如重定向到另一個(gè)頁(yè)面)。
[java] view plain copy print?
然后在shiro.ini中進(jìn)行如下配置:
[java] view plain copy print?
5、基于表單登錄攔截器
??? 之前我們已經(jīng)使用過(guò)Shiro內(nèi)置的基于表單登錄的攔截器了,此處自己做一個(gè)類(lèi)似的基于表單登錄的攔截器。
[java] view plain copy print?
onPreHandle主要流程:
??? 1、首先判斷是否已經(jīng)登錄過(guò)了,如果已經(jīng)登錄過(guò)了繼續(xù)攔截器鏈即可;
??? 2、如果沒(méi)有登錄,看看是否是登錄請(qǐng)求,如果是get方法的登錄頁(yè)面請(qǐng)求,則繼續(xù)攔截器鏈(到請(qǐng)求頁(yè)面),否則如果是get方法的其他頁(yè)面請(qǐng)求則保存當(dāng)前請(qǐng)求并重定向到登錄頁(yè)面;
??? 3、如果是post方法的登錄頁(yè)面表單提交請(qǐng)求,則收集用戶(hù)名/密碼登錄即可,如果失敗了保存錯(cuò)誤消息到“shiroLoginFailure”并返回到登錄頁(yè)面;
??? 4、如果登錄成功了,且之前有保存的請(qǐng)求,則重定向到之前的這個(gè)請(qǐng)求,否則到默認(rèn)的成功頁(yè)面。
shiro.ini配置
[java] view plain copy print?
??? 啟動(dòng)服務(wù)器輸入http://localhost:8080/chapter8/test.jsp測(cè)試時(shí),會(huì)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,登錄成功后又會(huì)跳回到test.jsp頁(yè)面。
??? 此處可以通過(guò)繼承AuthenticatingFilter實(shí)現(xiàn),其提供了很多登錄相關(guān)的基礎(chǔ)代碼。另外可以參考Shiro內(nèi)嵌的FormAuthenticationFilter的源碼,思路是一樣的。
6、任意角色授權(quán)攔截器
??? Shiro提供roles攔截器,其驗(yàn)證用戶(hù)擁有所有角色,沒(méi)有提供驗(yàn)證用戶(hù)擁有任意角色的攔截器。
[java] view plain copy print?
流程:
??? 1、首先判斷用戶(hù)有沒(méi)有任意角色,如果沒(méi)有返回false,將到onAccessDenied進(jìn)行處理;
??? 2、如果用戶(hù)沒(méi)有角色,接著判斷用戶(hù)有沒(méi)有登錄,如果沒(méi)有登錄先重定向到登錄;
??? 3、如果用戶(hù)沒(méi)有角色且設(shè)置了未授權(quán)頁(yè)面(unauthorizedUrl),那么重定向到未授權(quán)頁(yè)面;否則直接返回401未授權(quán)錯(cuò)誤碼。
shiro.ini配置
[java] view plain copy print?
??? 此處可以繼承AuthorizationFilter實(shí)現(xiàn),其提供了授權(quán)相關(guān)的基礎(chǔ)代碼。另外可以參考Shiro內(nèi)嵌的RolesAuthorizationFilter的源碼,只是實(shí)現(xiàn)hasAllRoles邏輯。
?
?
4 默認(rèn)攔截器
??? Shiro內(nèi)置了很多默認(rèn)的攔截器,比如身份驗(yàn)證、授權(quán)等相關(guān)的。默認(rèn)攔截器可以參考o(jì)rg.apache.shiro.web.filter.mgt.DefaultFilter中的枚舉攔截器:?
| 默認(rèn)攔截器名 | 攔截器類(lèi) | 說(shuō)明(括號(hào)里的表示默認(rèn)值) |
| 身份驗(yàn)證相關(guān)的 | ? | ? |
| authc | org.apache.shiro.web.filter.authc .FormAuthenticationFilter | 基于表單的攔截器;如“/**=authc”,如果沒(méi)有登錄會(huì)跳到相應(yīng)的登錄頁(yè)面登錄;主要屬性:usernameParam:表單提交的用戶(hù)名參數(shù)名( username); ?passwordParam:表單提交的密碼參數(shù)名(password); rememberMeParam:表單提交的密碼參數(shù)名(rememberMe);? loginUrl:登錄頁(yè)面地址(/login.jsp);successUrl:登錄成功后的默認(rèn)重定向地址; failureKeyAttribute:登錄失敗后錯(cuò)誤信息存儲(chǔ)key(shiroLoginFailure); |
| authcBasic | org.apache.shiro.web.filter.authc .BasicHttpAuthenticationFilter | Basic HTTP身份驗(yàn)證攔截器,主要屬性: applicationName:彈出登錄框顯示的信息(application); |
| logout | org.apache.shiro.web.filter.authc .LogoutFilter | 退出攔截器,主要屬性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout” |
| user | org.apache.shiro.web.filter.authc .UserFilter | 用戶(hù)攔截器,用戶(hù)已經(jīng)身份驗(yàn)證/記住我登錄的都可;示例“/**=user” |
| anon | org.apache.shiro.web.filter.authc .AnonymousFilter | 匿名攔截器,即不需要登錄即可訪(fǎng)問(wèn);一般用于靜態(tài)資源過(guò)濾;示例“/static/**=anon” |
| 授權(quán)相關(guān)的 | ? | ? |
| roles | org.apache.shiro.web.filter.authz .RolesAuthorizationFilter | 角色授權(quán)攔截器,驗(yàn)證用戶(hù)是否擁有所有角色;主要屬性: loginUrl:登錄頁(yè)面地址(/login.jsp);unauthorizedUrl:未授權(quán)后重定向的地址;示例“/admin/**=roles[admin]” |
| perms | org.apache.shiro.web.filter.authz .PermissionsAuthorizationFilter | 權(quán)限授權(quán)攔截器,驗(yàn)證用戶(hù)是否擁有所有權(quán)限;屬性和roles一樣;示例“/user/**=perms["user:create"]” |
| port | org.apache.shiro.web.filter.authz .PortFilter | 端口攔截器,主要屬性:port(80):可以通過(guò)的端口;示例“/test= port[80]”,如果用戶(hù)訪(fǎng)問(wèn)該頁(yè)面是非80,將自動(dòng)將請(qǐng)求端口改為80并重定向到該80端口,其他路徑/參數(shù)等都一樣 |
| rest | org.apache.shiro.web.filter.authz .HttpMethodPermissionFilter | rest風(fēng)格攔截器,自動(dòng)根據(jù)請(qǐng)求方法構(gòu)建權(quán)限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)構(gòu)建權(quán)限字符串;示例“/users=rest[user]”,會(huì)自動(dòng)拼出“user:read,user:create,user:update,user:delete”權(quán)限字符串進(jìn)行權(quán)限匹配(所有都得匹配,isPermittedAll); |
| ssl | org.apache.shiro.web.filter.authz .SslFilter | SSL攔截器,只有請(qǐng)求協(xié)議是https才能通過(guò);否則自動(dòng)跳轉(zhuǎn)會(huì)https端口(443);其他和port攔截器一樣; |
| 其他 | ? | ? |
| noSessionCreation | org.apache.shiro.web.filter.session .NoSessionCreationFilter | 不創(chuàng)建會(huì)話(huà)攔截器,調(diào)用 subject.getSession(false)不會(huì)有什么問(wèn)題,但是如果 subject.getSession(true)將拋出 DisabledSessionException異常; |
???? 另外還提供了一個(gè)org.apache.shiro.web.filter.authz.HostFilter,即主機(jī)攔截器,比如其提供了屬性:authorizedIps:已授權(quán)的ip地址,deniedIps:表示拒絕的ip地址;不過(guò)目前還沒(méi)有完全實(shí)現(xiàn),不可用。
??? 這些默認(rèn)的攔截器會(huì)自動(dòng)注冊(cè),可以直接在ini配置文件中通過(guò)“攔截器名.屬性”設(shè)置其屬性:
[java] view plain copy print?
另外如果某個(gè)攔截器不想使用了可以直接通過(guò)如下配置直接禁用:
[java] view plain copy print?
總結(jié)
以上是生活随笔為你收集整理的shiro学习(19): 拦截器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 科普:国产芯片、芯片生产设备、通讯标准前
- 下一篇: 可靠消息最终一致性设计_如何最终启动您的