国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
第?7?章?使用filter過(guò)濾請(qǐng)求

第 7 章 使用filter過(guò)濾請(qǐng)求

注意

Filter雖然很常用,但是覆蓋的范圍太廣,這里我們只介紹設(shè)置編碼和控制權(quán)限的過(guò)濾器,其他的使用方式還需要大家自行積累。

如果你不滿足以下任一條件,請(qǐng)繼續(xù)閱讀,否則請(qǐng)?zhí)^(guò)此后的部分,進(jìn)入下一章:第 8 章 配置listener監(jiān)聽(tīng)器

  1. 了解Filter的使用。

7.1. 批量設(shè)置請(qǐng)求編碼

編碼問(wèn)題會(huì)不會(huì)成為中國(guó)人學(xué)java的標(biāo)志呢?

通過(guò)之前的討論第 2.2.2 節(jié) “POST亂碼”,我們知道為了避免提交數(shù)據(jù)的亂碼問(wèn)題,需要在每次使用請(qǐng)求之前設(shè)置編碼格式。在你復(fù)制粘貼了無(wú)數(shù)次request.setCharacterEncoding("gb2312");后,有沒(méi)有想要一勞永逸的方法呢?能不能一次性修改所有請(qǐng)求的編碼呢?

用Filter吧,它的名字是過(guò)濾器,可以批量攔截修改servlet的請(qǐng)求和響應(yīng)。

我們編寫(xiě)一個(gè)EncodingFilter.java,來(lái)批量設(shè)置請(qǐng)求編碼。

package anni;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class EncodingFilter implements Filter {
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("gb2312");
chain.doFilter(request, response);
}
}

在此EncodingFilter實(shí)現(xiàn)了Filter接口,F(xiàn)ilter接口中定義的三個(gè)方法都要在EncodingFilter中實(shí)現(xiàn),其中doFilter()的代碼實(shí)現(xiàn)主要的功能:為請(qǐng)求設(shè)置gb2312編碼并執(zhí)行chain.doFilter()繼續(xù)下面的操作。

與servlet相似,為了讓filter發(fā)揮作用還需要在web.xml進(jìn)行配置。

<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>anni.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

filter標(biāo)簽部分定義使用的過(guò)濾器,filter-mapping標(biāo)簽告訴服務(wù)器把哪些請(qǐng)求交給過(guò)濾器處理。這里的/*表示所有請(qǐng)求,/表示根路徑,*(星號(hào))代表所有請(qǐng)求,加在一起就變成了根路徑下的所有請(qǐng)求。

這樣,所有的請(qǐng)求都會(huì)先被EncodingFilter攔截,并在請(qǐng)求里設(shè)置上指定的gb2312編碼。

例子在lingo-sample/07-01目錄下,這次我們不需要在test.jsp中為請(qǐng)求設(shè)置編碼也可以得到正常的中文參數(shù)了,EncodingFilter圓滿的完成了它的工作。

7.2. 用filter控制用戶訪問(wèn)權(quán)限

出于信息安全和其他一些原因的考慮,項(xiàng)目中的一些頁(yè)面要求用戶滿足了一定條件之后才能訪問(wèn)。比如,讓用戶輸入賬號(hào)和密碼,如果輸入的信息正確就在session里做一個(gè)成功登錄的標(biāo)記,其后在請(qǐng)求保密信息的時(shí)候判斷session中是否有已經(jīng)登錄成功的標(biāo)記,存在則可以訪問(wèn),不存在則禁止訪問(wèn)。

如07-02例子中所示,進(jìn)入首頁(yè)看到的就是登錄頁(yè)面。

現(xiàn)在用戶還沒(méi)有登錄,如果直接訪問(wèn)保密信息,就會(huì)顯示無(wú)法訪問(wèn)保密信息的頁(yè)面,并提醒用戶進(jìn)行注冊(cè)。

返回登錄頁(yè)面后,輸入正確的用戶名和密碼,點(diǎn)擊登錄。

后臺(tái)程序判斷用戶名和密碼正確無(wú)誤后,在session中設(shè)置已登錄的標(biāo)記,然后跳轉(zhuǎn)到保密信息頁(yè)面。

我們要保護(hù)的頁(yè)面是admin/index.jsp,為此我們?cè)趙eb.xml進(jìn)行如下配置。

<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>anni.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

定義SecurityFilter過(guò)濾器,讓它過(guò)濾匹配/admin/*的所有請(qǐng)求,這就是說(shuō),對(duì)/admin/路徑下的所有請(qǐng)求都會(huì)接受SecurityFilter的檢查,那么SecurityFilter里到底做了些什么呢?

public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
if (session.getAttribute("username") != null) {
chain.doFilter(request, response);
} else {
res.sendRedirect("../failure.jsp");
}
}

首先要將ServletRequest和ServletResponse轉(zhuǎn)換成HttpServletRequest和HttpServletResponse,因?yàn)镕ilter本來(lái)設(shè)計(jì)成為多種協(xié)議服務(wù),http協(xié)議僅僅是其中一部分。不過(guò)我們接觸到的也只有http,而且也只有轉(zhuǎn)換成對(duì)應(yīng)HttpServletRequest和HttpServletResponse才能進(jìn)行下面的session操作和頁(yè)面重定向。

得到了http請(qǐng)求之后,可以獲得請(qǐng)求對(duì)應(yīng)的session,判斷session中的username變量是否為null,如果不為null,說(shuō)明用戶已經(jīng)登錄,就可以調(diào)用doFilter繼續(xù)請(qǐng)求訪問(wèn)的資源。如果為null,說(shuō)明用戶還沒(méi)有登錄,禁止用戶訪問(wèn),并使用頁(yè)面重定向跳轉(zhuǎn)到failure.jsp頁(yè)面顯示提示信息。

session中的username實(shí)在登錄的時(shí)候設(shè)置進(jìn)去的,值就是登錄用戶使用的用戶名,詳細(xì)代碼可以參考07-02/WEB-INF/src/LoginServlet.java,登錄和注銷都寫(xiě)成了servlet并映射到/login.do和/logout.do這兩個(gè)請(qǐng)求路徑上。源代碼和web.xml配置請(qǐng)自行參考07-02中的例子,這里就不復(fù)述了。

我們?cè)賮?lái)看看頁(yè)面重定向的寫(xiě)法,res.sendRedirect()中使用的是"../failure.jsp",兩個(gè)點(diǎn)(..)代表當(dāng)前路徑的上一級(jí)路徑,這是因?yàn)镾ecurityFilter負(fù)責(zé)處理的是/admin/下的請(qǐng)求,而/failure.jsp的位置在/admin/目錄的上一級(jí),所以加上兩個(gè)點(diǎn)才能正確跳轉(zhuǎn)到failure.jsp。當(dāng)然這里使用forward()也可以,但是要注意在不同路徑下做請(qǐng)求轉(zhuǎn)發(fā)會(huì)影響頁(yè)面中相對(duì)路徑的指向。相關(guān)討論在:第 3.4.2 節(jié) “forward導(dǎo)致找不到圖片”。

7.3. filter所謂的特性

7.3.1. 請(qǐng)求映射

filter-mapping和servlet-mapping都是將對(duì)應(yīng)的filter或servlet映射到某個(gè)url-pattern上,當(dāng)客戶發(fā)起某一請(qǐng)求時(shí),服務(wù)器先將此請(qǐng)求與web.xml中定義的所有url-pattern進(jìn)行匹配,然后執(zhí)行匹配通過(guò)的filter和servlet。

你可以使用三種方式定義url-pattern。

  1. 直接映射一個(gè)請(qǐng)求。

    <servlet-mapping>
        <servlet-name>ContactServlet</servlet-name>
        <url-pattern>/contact.do</url-pattern>
        </servlet-mapping>
        

    第 6.3 節(jié) “使用servlet改寫(xiě)聯(lián)系簿”中對(duì)servlet的映射,只有當(dāng)請(qǐng)求是/contact.do的時(shí)候才會(huì)執(zhí)行ContactServlet。/contact.do?id=1或/contact.do?method=list&id=1的請(qǐng)求也可以匹配到ContactServlet,這是因?yàn)楦鶕?jù)http規(guī)范,請(qǐng)求的路徑不包含問(wèn)號(hào)以后的部分。

  2. 映射一個(gè)路徑下的所有請(qǐng)求。

    <servlet-mapping>
        <servlet-name>EncodingFilter</servlet-name>
        <url-pattern>/*</url-pattern>
        </servlet-mapping>
        

    第 7.1 節(jié) “批量設(shè)置請(qǐng)求編碼”中這樣使用星號(hào)(*)的形式,可以將某個(gè)路徑下的所有請(qǐng)求都映射到EncodingFilter過(guò)濾器下,如果這個(gè)路徑下還有子路徑,那么子路徑下的請(qǐng)求也會(huì)被EncodingFilter過(guò)濾。所以 /*這種寫(xiě)法就會(huì)過(guò)濾應(yīng)用下所有的請(qǐng)求。

    如果像第 7.2 節(jié) “用filter控制用戶訪問(wèn)權(quán)限”中那樣把映射配置成/admin/*,就會(huì)只處理/admin/路徑下的請(qǐng)求,不會(huì)處理根路徑下的/index.jsp和/failure.jsp。

    需要注意的是,這種寫(xiě)法必須以/開(kāi)頭,寫(xiě)成與絕對(duì)路徑的形式,即便是映射所有請(qǐng)求也要寫(xiě)成/*,不能簡(jiǎn)化成*。

  3. 映射結(jié)尾相同的一類請(qǐng)求。

    <servlet-mapping>
        <servlet-name>ControllerServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
        </servlet-mapping>
        

    具體效果請(qǐng)參考07-03的例子,index.jsp中有四個(gè)鏈接,分別指向/a1.do, /a2.do, /xx/b1.do, /xx/yy/c1.do。

    web.xml中的ControllerServlet會(huì)接收以.do結(jié)尾的請(qǐng)求,并使用forward將請(qǐng)求轉(zhuǎn)發(fā)到/test.jsp。

    點(diǎn)擊/a1.do的情況。

    點(diǎn)擊/xx/yy/c1.do的情況。

    這樣做的一個(gè)好處是語(yǔ)義更清楚,只要看到以.do結(jié)尾的請(qǐng)求就知道肯定是交給ControllerServlet處理了,不管這個(gè)請(qǐng)求是在根路徑還是子路徑下,都會(huì)準(zhǔn)確無(wú)誤的找到對(duì)應(yīng)的servlet。

    缺點(diǎn)就是不同路徑之間進(jìn)行forward,jsp里就不能再使用相對(duì)路徑了,所以我們?cè)趖est.jsp中使用request.getContextPath()獲得當(dāng)前應(yīng)用在服務(wù)器中的位置(例子中是/07-03)將相對(duì)路徑都組裝成絕對(duì)路徑,這種用法在以后也會(huì)經(jīng)常用到。

    <%
        pageContext.setAttribute("ctx", request.getContextPath());
        %>
        <p><a href="${ctx}/index.jsp">返回</a></p>
        

    最后需要注意的是,這種請(qǐng)求映射就不能指定某一路徑了,它必須是以星號(hào)(*)開(kāi)始字母結(jié)尾,不能寫(xiě)成/*.do的形式。

現(xiàn)在咱們也發(fā)現(xiàn)java的請(qǐng)求映射有多傻了,靈活配置根本是不可能的任務(wù)。

想要獲得所有以u(píng)ser開(kāi)頭.do結(jié)尾的請(qǐng)求嗎?user*.do在url-pattern是無(wú)法識(shí)別的,只能配置成*.do,再去servlet中對(duì)請(qǐng)求進(jìn)行篩選。

想要讓一個(gè)servlet負(fù)責(zé)多個(gè)請(qǐng)求嗎?/user/*,/admin/*,*.do寫(xiě)在一起url-pattern也不認(rèn)識(shí),只能配成多個(gè)servlet-mapping。

<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

java的復(fù)雜性在此處顯露無(wú)疑。實(shí)際使用時(shí),最好不要依賴web.xml中的配置,在自己的類中實(shí)現(xiàn)靈活配置才是正途。

7.3.2. 過(guò)濾鏈

其實(shí)在07-02這個(gè)例子里,我們使用了兩個(gè)過(guò)濾器,EncodingFilter負(fù)責(zé)設(shè)置編碼,SecurityFilter負(fù)責(zé)控制權(quán)限,那這兩個(gè)過(guò)濾器是怎么起作用的呢?它們兩個(gè)同時(shí)過(guò)濾一個(gè)請(qǐng)求時(shí)誰(shuí)先誰(shuí)后呢?

下面這個(gè)圖會(huì)告訴我們答案。

所有的奧秘就在Filter中的FilterChain中。服務(wù)器會(huì)按照web.xml中過(guò)濾器定義的先后循序組裝成一條鏈,然后一次執(zhí)行其中的doFilter()方法。執(zhí)行的順序就如上圖所示,執(zhí)行第一個(gè)過(guò)濾器的chain.doFilter()之前的代碼,第二個(gè)過(guò)濾器的chain.doFilter()之前的代碼,請(qǐng)求的資源,第二個(gè)過(guò)濾器的chain.doFilter()之后的代碼,第一個(gè)過(guò)濾器的chain.doFilter()之后的代碼,最后返回響應(yīng)。

因此在07-02中執(zhí)行的代碼順序是:

  1. 執(zhí)行EncodingFilter.doFilter()中chain.doFilter()之前的部分:request.setCharacterEncoding("gb2312");

  2. 執(zhí)行SecurityFilter.doFilter()中chain.doFilter()之前的部分:判斷用戶是否已登錄。

    如果用戶已登錄,則訪問(wèn)請(qǐng)求的資源:/admin/index.jsp。

    如果用戶未登錄,則頁(yè)面重定向到:/failure.jsp。

  3. 執(zhí)行SecurityFilter.doFilter()中chain.doFilter()之后的部分:這里沒(méi)有代碼。

  4. 執(zhí)行EncodingFilter.doFilter()中chain.doFilter()之后的部分:這里也沒(méi)有代碼。

過(guò)濾鏈的好處是,執(zhí)行過(guò)程中任何時(shí)候都可以打斷,只要不執(zhí)行chain.doFilter()就不會(huì)再執(zhí)行后面的過(guò)濾器和請(qǐng)求的內(nèi)容。而在實(shí)際使用時(shí),就要特別注意過(guò)濾鏈的執(zhí)行順序問(wèn)題,像EncodingFilter就一定要放在所有Filter之前,這樣才能確保在使用請(qǐng)求中的數(shù)據(jù)前設(shè)置正確的編碼。

7.4. filter的詳細(xì)配置

我們已經(jīng)了解了filter的基本用法,還有一些細(xì)節(jié)配置在特殊情況下起作用。

在servlet-2.3中,F(xiàn)ilter會(huì)過(guò)濾一切請(qǐng)求,包括服務(wù)器內(nèi)部使用forward轉(zhuǎn)發(fā)請(qǐng)求和<%@ include file="/index.jsp"%>的情況。

到了servlet-2.4中Filter默認(rèn)下只攔截外部提交的請(qǐng)求,forward和include這些內(nèi)部轉(zhuǎn)發(fā)都不會(huì)被過(guò)濾,但是有時(shí)候我們需要forward的時(shí)候也用到Filter,這樣就需要如下配置。

<filter>
<filter-name>TestFilter</filtername>
<filter-class>anni.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filtername>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>EXCEPTION</dispatcher>
</filter-mapping>

這樣TestFilter就會(huì)過(guò)濾所有狀態(tài)下的請(qǐng)求。如果我們沒(méi)有進(jìn)行設(shè)置,默認(rèn)使用的就是REQUEST。而EXCEPTION是在isErrorPage="true"的情況下出現(xiàn)的,這個(gè)用處不多,看一下即可。

這里FORWARD是解決request.getDispatcher("index.jsp").forward(request, response);無(wú)法觸發(fā)Filter的關(guān)鍵,配置上這個(gè)以后再進(jìn)行forward的時(shí)候就可以觸發(fā)過(guò)濾器了。

Filter還有一個(gè)有趣的用法,在filter-mapping中我們可以直接指定servlet-mapping,讓過(guò)濾器只處理一個(gè)定義在web.xml中的servlet。

<filter-mapping>
<filter-name>TestFilter</filter-name>
<servlet-name>TestServlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>anni.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
filter
過(guò)濾器之中文亂碼解決
Web.xml中Filter過(guò)濾器標(biāo)簽幾個(gè)說(shuō)明
通過(guò)過(guò)濾器獲取暗號(hào)6--xml
Servlet從了解到放棄(08)
Resin 的mod_caucho插件不解析filter的問(wèn)題
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服