url过滤怎么解除_Shiro-实战(四)---过滤器机制
1 簡(jiǎn)介
Shiro使用了與Servlet一樣的Filter接口進(jìn)行擴(kuò)展
1.1 NameableFilter
NameableFilter給Filter起個(gè)名字,如果沒有設(shè)置默認(rèn)就是FilterName 當(dāng)我們組裝攔截器鏈時(shí)會(huì)根據(jù)這個(gè)名字找到相應(yīng)的攔截器實(shí)例
1.2 OncePerRequestFilter
用于防止多次執(zhí)行Filter,也就是說(shuō)一次請(qǐng)求只會(huì)走一次攔截器鏈 enabled屬性:是否開啟該攔截器,默認(rèn)enabled=true表開啟
1.3 ShiroFilter
整個(gè)Shiro的入口點(diǎn),用于攔截需要安全控制的請(qǐng)求進(jìn)行處理
1.4 AdviceFilter
提供AOP風(fēng)格的支持,類似于SpringMVC中的Interceptor - preHandler:類AOP前置增強(qiáng) 在攔截器鏈執(zhí)行之前執(zhí)行;如果返回true則繼續(xù)攔截器鏈 否則中斷后續(xù)的攔截器鏈的執(zhí)行直接返回 進(jìn)行預(yù)處理(如基于表單的身份驗(yàn)證、授權(quán)) - postHandle:類AOP后置返回增強(qiáng) 在攔截器鏈執(zhí)行完成后執(zhí)行 進(jìn)行后處理(如記錄執(zhí)行時(shí)間之類的); - afterCompletion:類AOP后置最終增強(qiáng) 不管有沒有異常都會(huì)執(zhí)行 可以進(jìn)行清理資源(如解除Subject與線程的綁定之類的)
1.5 PathMatchingFilter
提供基于Ant風(fēng)格的請(qǐng)求路徑匹配功能及攔截器參數(shù)解析的功能,如roles[admin,user]自動(dòng)根據(jù),分割解析到一個(gè)路徑參數(shù)配置并綁定到相應(yīng)的路徑 - pathsMatch
用于path與請(qǐng)求路徑進(jìn)行匹配的方法,如果匹配返回true - onPreHandle(待探討)
在preHandle中,當(dāng)pathsMatch匹配一個(gè)路徑后,會(huì)調(diào)用onPreHandler方法并將路徑綁定參數(shù)配置傳給mappedValue;然后可以在這個(gè)方法中進(jìn)行一些驗(yàn)證(如角色授權(quán)),如果驗(yàn)證失敗可以返回false中斷流程;默認(rèn)返回true;也就是說(shuō)子類可以只實(shí)現(xiàn)onPreHandle即可,無(wú)須實(shí)現(xiàn)preHandle。如果沒有path與請(qǐng)求路徑匹配,默認(rèn)是通過的(即preHandle返回true)
1.6 AccessControlFilter
提供訪問控制的基礎(chǔ)功能;比如是否允許訪問/當(dāng)訪問拒絕時(shí)如何處理等 - isAccessAllowed
表是否允許訪問;mappedValue就是[urls]配置中攔截器參數(shù)部分,如果允許訪問返回true,否則false - onAccessDenied
表當(dāng)訪問拒絕時(shí)是否已經(jīng)處理 如果返回true表示需要繼續(xù)處理 如果返回false表示該攔截器實(shí)例已經(jīng)處理了,直接返回即可
onPreHandle會(huì)自動(dòng)調(diào)用這兩個(gè)方法決定是否繼續(xù)處理
AccessControlFilter還提供了如下方法用于處理如登錄成功后/重定向到上一個(gè)請(qǐng)求
void setLoginUrl(String loginUrl) //身份驗(yàn)證時(shí)使用,默認(rèn)/login.jsp String getLoginUrl() Subject getSubject(ServletRequest request, ServletResponse response) //獲取Subject實(shí)例 boolean isLoginRequest(ServletRequest request, ServletResponse response)//當(dāng)前請(qǐng)求是否是登錄請(qǐng)求 void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException //將當(dāng)前請(qǐng)求保存起來(lái)并重定向到登錄頁(yè)面 void saveRequest(ServletRequest request) //將請(qǐng)求保存起來(lái),如登錄成功后再重定向回該請(qǐng)求 void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登錄頁(yè)面比如基于表單的身份驗(yàn)證就需要使用這些功能
到此基本的攔截器完結(jié) 若我們想進(jìn)行訪問控制就可以繼承AccessControlFilter 若我們要添加一些通用數(shù)據(jù)我們可以直接繼承PathMatchingFilter
2 過濾器鏈
2.1 簡(jiǎn)介
Shiro對(duì)Servlet容器的FilterChain進(jìn)行了代理,即ShiroFilter在繼續(xù)Servlet容器的Filter鏈的執(zhí)行之前,通過ProxiedFilterChain對(duì)Servlet容器的FilterChain進(jìn)行了代理 即先走Shiro自己的Filter體系,然后才會(huì)委托給Servlet容器的FilterChain進(jìn)行Servlet容器級(jí)別的Filter鏈執(zhí)行 Shiro的ProxiedFilterChain執(zhí)行流程 - 先執(zhí)行Shiro自己的Filter鏈 - 再執(zhí)行Servlet容器的Filter鏈(即原始的Filter)
而ProxiedFilterChain是通過FilterChainResolver根據(jù)配置文件中[urls]部分是否與請(qǐng)求的URL是否匹配解析得到的
即傳入原始的chain得到一個(gè)代理的chain
Shiro內(nèi)部提供了一個(gè)路徑匹配的FilterChainResolver實(shí)現(xiàn):PathMatchingFilterChainResolver 其根據(jù)[urls]中配置的url模式(默認(rèn)Ant風(fēng)格) 即根據(jù)過濾器鏈和請(qǐng)求的url是否匹配來(lái)解析得到配置的過濾器鏈 而PathMatchingFilterChainResolver內(nèi)部通過FilterChainManager
維護(hù)過濾器鏈 比如DefaultFilterChainManager
維護(hù)著url模式與過濾器鏈的關(guān)系 因此我們可以通過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中聲明的過濾器
2.2 注冊(cè)自定義攔截器
IniSecurityManagerFactory/WebIniSecurityManagerFactory在啟動(dòng)時(shí)會(huì)自動(dòng)掃描ini配置文件中的[filters]/[main]部分并注冊(cè)這些攔截器到DefaultFilterChainManager 且創(chuàng)建相應(yīng)的url模式與其攔截器關(guān)系鏈
如果想自定義FilterChainResolver,可以通過實(shí)現(xiàn)WebEnvironment接口完成
public class MyIniWebEnvironment extends IniWebEnvironment { @Override protected FilterChainResolver createFilterChainResolver() { //在此處擴(kuò)展自己的FilterChainResolver return super.createFilterChainResolver(); } }FilterChain之間的關(guān)系。如果想動(dòng)態(tài)實(shí)現(xiàn)url-攔截器的注冊(cè),就可以通過實(shí)現(xiàn)此處的FilterChainResolver來(lái)完成,比如:
//1、創(chuàng)建FilterChainResolver PathMatchingFilterChainResolver filterChainResolver = new PathMatchingFilterChainResolver(); //2、創(chuàng)建FilterChainManager DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager(); //3、注冊(cè)Filter for(DefaultFilter filter : DefaultFilter.values()) { filterChainManager.addFilter( filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass())); } //4、注冊(cè)URL-Filter的映射關(guān)系 filterChainManager.addToChain("/login.jsp", "authc"); filterChainManager.addToChain("/unauthorized.jsp", "anon"); filterChainManager.addToChain("/**", "authc"); filterChainManager.addToChain("/**", "roles", "admin"); //5、設(shè)置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;此處自己去實(shí)現(xiàn)注冊(cè)filter,及url模式與filter之間的映射關(guān)系 可以通過定制FilterChainResolver或FilterChainManager來(lái)完成諸如動(dòng)態(tài)URL匹配的實(shí)現(xiàn)
然后再web.xml中進(jìn)行如下配置Environment
<context-param> <param-name>shiroEnvironmentClass</param-name> <param-value>com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment</param-value> </context-param>2.3 自定義過濾器
通過自定義自己的過濾器可以擴(kuò)展一些功能,諸如動(dòng)態(tài)url-角色/權(quán)限訪問控制的實(shí)現(xiàn)、根據(jù)Subject身份信息獲取用戶信息綁定到Request(即設(shè)置通用數(shù)據(jù))、驗(yàn)證碼驗(yàn)證、在線用戶信息的保存等等,因?yàn)槠浔举|(zhì)就是一個(gè)Filter;所以Filter能做的它就能做
2.3.1 擴(kuò)展OncePerRequestFilter
OncePerRequestFilter保證一次請(qǐng)求只調(diào)用一次doFilterInternal,即如內(nèi)部的forward不會(huì)再多執(zhí)行一次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] /**=myFilter1Filter可以在[main]或[filters]部分注冊(cè),然后在[urls]部分配置url與filter的映射關(guān)系
2.3.2 擴(kuò)展AdviceFilter
AdviceFilter提供了AOP的功能,其實(shí)現(xiàn)和SpringMVC中的Interceptor思想一樣
public class MyAdviceFilter extends AdviceFilter { @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { System.out.println("====預(yù)處理/前置處理"); return true;//返回false將中斷后續(xù)攔截器鏈的執(zhí)行 } @Override protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { System.out.println("====后處理/后置返回處理"); } @Override public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { System.out.println("====完成處理/后置最終處理"); } }shiro.ini配置
[filters] myFilter1=com.sss.web.filter.MyOncePerRequestFilter myFilter2=com.sss.web.filter.MyAdviceFilter [urls] /**=myFilter1,myFilter22.3.3 PathMatchingFilter
PathMatchingFilter繼承了AdviceFilter,提供了url模式過濾的功能,如果需要對(duì)指定的請(qǐng)求進(jìn)行處理,可以擴(kuò)展PathMatchingFilter
public class MyPathMatchingFilter extends PathMatchingFilter { @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { System.out.println("url matches,config is " + Arrays.toString((String[])mappedValue)); return true; } }- preHandle:會(huì)進(jìn)行url模式與請(qǐng)求url進(jìn)行匹配,如果匹配會(huì)調(diào)用onPreHandle;如果沒有配置url模式/沒有url模式匹配,默認(rèn)直接返回true;
- onPreHandle:如果url模式與請(qǐng)求url匹配,那么會(huì)執(zhí)行onPreHandle,并把該攔截器配置的參數(shù)傳入。默認(rèn)什么不處理直接返回true。
shiro.ini配置
[filters] myFilter3=com.sss.web.filter.MyPathMatchingFilter [urls] /**= myFilter3[config]/**就是注冊(cè)給PathMatchingFilter的url模式,config就是攔截器的配置參數(shù),多個(gè)之間逗號(hào)分隔,onPreHandle使用mappedValue接收參數(shù)值。
2.3.4 擴(kuò)展AccessControlFilter
AccessControlFilter繼承了PathMatchingFilter,并擴(kuò)展了了兩個(gè)方法
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue); }- isAccessAllowed:即是否允許訪問,返回true表示允許;
- onAccessDenied:表示訪問拒絕時(shí)是否自己處理,如果返回true表示自己不處理且繼續(xù)攔截器鏈執(zhí)行,返回false表示自己已經(jīng)處理了(比如重定向到另一個(gè)頁(yè)面)
shiro.ini配置
[filters] myFilter4=com.sss.web.filter.MyAccessControlFilter [urls] /**=myFilter42.3.5 基于表單登錄過濾器
public class FormLoginFilter extends PathMatchingFilter { private String loginUrl = "/login.jsp"; private String successUrl = "/"; @Override protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if(SecurityUtils.getSubject().isAuthenticated()) { return true;//已經(jīng)登錄過 } HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if(isLoginRequest(req)) { if("post".equalsIgnoreCase(req.getMethod())) {//form表單提交 boolean loginSuccess = login(req); //登錄 if(loginSuccess) { return redirectToSuccessUrl(req, resp); } } return true;//繼續(xù)過濾器鏈 } else {//保存當(dāng)前地址并重定向到登錄界面 saveRequestAndRedirectToLogin(req, resp); return false; } } private boolean redirectToSuccessUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException { WebUtils.redirectToSavedRequest(req, resp, successUrl); return false; } private void saveRequestAndRedirectToLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException { WebUtils.saveRequest(req); WebUtils.issueRedirect(req, resp, loginUrl); } private boolean login(HttpServletRequest req) { String username = req.getParameter("username"); String password = req.getParameter("password"); try { SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password)); } catch (Exception e) { req.setAttribute("shiroLoginFailure", e.getClass()); return false; } return true; } private boolean isLoginRequest(HttpServletRequest req) { return pathsMatch(loginUrl, WebUtils.getPathWithinApplication(req)); } }onPreHandle主要流程: - 首先判斷是否已經(jīng)登錄過了,如果已經(jīng)登錄過了繼續(xù)攔截器鏈即可; - 如果沒有登錄,看看是否是登錄請(qǐng)求,如果是get方法的登錄頁(yè)面請(qǐng)求,則繼續(xù)攔截器鏈(到請(qǐng)求頁(yè)面),否則如果是get方法的其他頁(yè)面請(qǐng)求則保存當(dāng)前請(qǐng)求并重定向到登錄頁(yè)面; - 如果是post方法的登錄頁(yè)面表單提交請(qǐng)求,則收集用戶名/密碼登錄即可,如果失敗了保存錯(cuò)誤消息到“shiroLoginFailure”并返回到登錄頁(yè)面; - 如果登錄成功了,且之前有保存的請(qǐng)求,則重定向到之前的這個(gè)請(qǐng)求,否則到默認(rèn)的成功頁(yè)面
shiro.ini配置
[filters] formLogin=com.sss.web.filter.FormLoginFilter [urls] /test.jsp=formLogin /login.jsp=formLogin啟動(dòng)服務(wù)器輸入http://localhost:8080/sss/test.jsp測(cè)試時(shí),會(huì)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,登錄成功后又會(huì)跳回到test.jsp頁(yè)面。
此處可以通過繼承AuthenticatingFilter實(shí)現(xiàn),其提供了很多登錄相關(guān)的基礎(chǔ)代碼。另外可以參考Shiro內(nèi)嵌的FormAuthenticationFilter的源碼,思路是一樣的。
2.3.6 任意角色授權(quán)攔截器
Shiro提供roles攔截器,其驗(yàn)證用戶擁有所有角色,沒有提供驗(yàn)證用戶擁有任意角色的攔截器。
public class AnyRolesFilter extends AccessControlFilter { private String unauthorizedUrl = "/unauthorized.jsp"; private String loginUrl = "/login.jsp"; protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { String[] roles = (String[])mappedValue; if(roles == null) { return true;//如果沒有設(shè)置角色參數(shù),默認(rèn)成功 } for(String role : roles) { if(getSubject(request, response).hasRole(role)) { return true; } } return false;//跳到onAccessDenied處理 } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { Subject subject = getSubject(request, response); if (subject.getPrincipal() == null) {//表示沒有登錄,重定向到登錄頁(yè)面 saveRequest(request); WebUtils.issueRedirect(request, response, loginUrl); } else { if (StringUtils.hasText(unauthorizedUrl)) {//如果有未授權(quán)頁(yè)面跳轉(zhuǎn)過去 WebUtils.issueRedirect(request, response, unauthorizedUrl); } else {//否則返回401未授權(quán)狀態(tài)碼 WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); } } return false; } }- 首先判斷用戶有沒有任意角色,如果沒有返回false,將到onAccessDenied進(jìn)行處理;
- 如果用戶沒有角色,接著判斷用戶有沒有登錄,如果沒有登錄先重定向到登錄;
- 如果用戶沒有角色且設(shè)置了未授權(quán)頁(yè)面(unauthorizedUrl),那么重定向到未授權(quán)頁(yè)面;否則直接返回401未授權(quán)錯(cuò)誤碼。
shiro.ini配置
[filters] anyRoles=com.sss.web.filter.AnyRolesFilter [urls] /test.jsp=formLogin,anyRoles[admin,user] /login.jsp=formLogin此處可以繼承AuthorizationFilter實(shí)現(xiàn),其提供了授權(quán)相關(guān)的基礎(chǔ)代碼。另外可以參考Shiro內(nèi)嵌的RolesAuthorizationFilter的源碼,只是實(shí)現(xiàn)hasAllRoles邏輯。
3 默認(rèn)過濾器
Shiro內(nèi)置了很多默認(rèn)的過濾器,比如身份驗(yàn)證、授權(quán)等相關(guān)的。默認(rèn)攔截器可以參考o(jì)rg.apache.shiro.web.filter.mgt.DefaultFilter中的枚舉過濾器:
另外還提供了一個(gè)org.apache.shiro.web.filter.authz.HostFilter,即主機(jī)攔截器,比如其提供了屬性:authorizedIps:已授權(quán)的ip地址,deniedIps:表示拒絕的ip地址;不過目前還沒有完全實(shí)現(xiàn),不可用。
這些默認(rèn)的攔截器會(huì)自動(dòng)注冊(cè),可以直接在ini配置文件中通過“攔截器名.屬性”設(shè)置其屬性
perms.unauthorizedUrl=/unauthorized另外如果某個(gè)攔截器不想使用了可以直接通過如下配置直接禁用
perms.enabled=false總結(jié)
以上是生活随笔為你收集整理的url过滤怎么解除_Shiro-实战(四)---过滤器机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 焦作的计算机三级考试考点,3月河南计算机
- 下一篇: 安卓按钮设置背景颜色不管用_MIUI10