摘要:我們很容易發(fā)現(xiàn),過(guò)濾器可以比喻成一張濾網(wǎng)。這究竟是怎么回事啊我們可以這樣理解過(guò)濾器不單單只有一個(gè),那么我們?cè)趺垂芾磉@些過(guò)濾器呢在中就使用了鏈?zhǔn)浇Y(jié)構(gòu)。第一種方式在文件中配置用于注冊(cè)過(guò)濾器用于為過(guò)濾器指定一個(gè)名字,該元素的內(nèi)容不能為空。
什么是過(guò)濾器
過(guò)濾器是Servlet的高級(jí)特性之一,也別把它想得那么高深,只不過(guò)是實(shí)現(xiàn)Filter接口的Java類罷了!
首先,我們來(lái)看看過(guò)濾器究竟Web容器的哪處:
從上面的圖我們可以發(fā)現(xiàn),當(dāng)瀏覽器發(fā)送請(qǐng)求給服務(wù)器的時(shí)候,先執(zhí)行過(guò)濾器,然后才訪問(wèn)Web的資源。服務(wù)器響應(yīng)Response,從Web資源抵達(dá)瀏覽器之前,也會(huì)途徑過(guò)濾器。。
我們很容易發(fā)現(xiàn),過(guò)濾器可以比喻成一張濾網(wǎng)。我們想想現(xiàn)實(shí)中的濾網(wǎng)可以做什么:在泡茶的時(shí)候,過(guò)濾掉茶葉。那濾網(wǎng)是怎么過(guò)濾茶葉的呢?規(guī)定大小的網(wǎng)孔,只要網(wǎng)孔比茶葉小,就可以實(shí)現(xiàn)過(guò)濾了!
引申在Web容器中,過(guò)濾器可以做:過(guò)濾一些敏感的字符串【規(guī)定不能出現(xiàn)敏感字符串】、避免中文亂碼【規(guī)定Web資源都使用UTF-8編碼】、權(quán)限驗(yàn)證【規(guī)定只有帶Session或Cookie的瀏覽器,才能訪問(wèn)web資源】等等等,過(guò)濾器的作用非常大,只要發(fā)揮想象就可以有意想不到的效果
也就是說(shuō):當(dāng)需要限制用戶訪問(wèn)某些資源時(shí)、在處理請(qǐng)求時(shí)提前處理某些資源、服務(wù)器響應(yīng)的內(nèi)容對(duì)其進(jìn)行處理再返回、我們就是用過(guò)濾器來(lái)完成的!
為什么需要用到過(guò)濾器直接舉例子來(lái)說(shuō)明吧:
沒(méi)有過(guò)濾器解決中文亂碼問(wèn)題如果我沒(méi)有用到過(guò)濾器:瀏覽器通過(guò)http請(qǐng)求發(fā)送數(shù)據(jù)給Servlet,如果存在中文,就必須指定編碼,否則就會(huì)亂碼!
jsp頁(yè)面提交中文數(shù)據(jù)給Servlet處理
Servlet沒(méi)有指定編碼的情況下,獲取得到的是亂碼
Servlet中如何解決中文亂碼問(wèn)題,我的其他博文中有:http://blog.csdn.net/hon_3y/article/details/54632004
也就是說(shuō):如果我每次接受客戶端帶過(guò)來(lái)的中文數(shù)據(jù),在Serlvet中都要設(shè)定編碼。這樣代碼的重復(fù)率太高了!!!!
有過(guò)濾器解決中文亂碼問(wèn)題有過(guò)濾器的情況就不一樣了:只要我在過(guò)濾器中指定了編碼,可以使全站的Web資源都是使用該編碼,并且重用性是非常理想的!
過(guò)濾器 API只要Java類實(shí)現(xiàn)了Filter接口就可以稱為過(guò)濾器!Filter接口的方法也十分簡(jiǎn)單:
其中init()和destory()方法就不用多說(shuō)了,他倆跟Servlet是一樣的。只有在Web服務(wù)器加載和銷毀的時(shí)候被執(zhí)行,只會(huì)被執(zhí)行一次!
值得注意的是doFilter()方法,它有三個(gè)參數(shù)(ServletRequest,ServletResponse,FilterChain),從前兩個(gè)參數(shù)我們可以發(fā)現(xiàn):過(guò)濾器可以完成任何協(xié)議的過(guò)濾操作!
那FilterChain是什么東西呢?我們看看:
FilterChain是一個(gè)接口,里面又定義了doFilter()方法。這究竟是怎么回事啊??????
我們可以這樣理解:過(guò)濾器不單單只有一個(gè),那么我們?cè)趺垂芾磉@些過(guò)濾器呢?在Java中就使用了鏈?zhǔn)浇Y(jié)構(gòu)。把所有的過(guò)濾器都放在FilterChain里邊,如果符合條件,就執(zhí)行下一個(gè)過(guò)濾器(如果沒(méi)有過(guò)濾器了,就執(zhí)行目標(biāo)資源)。
上面的話好像有點(diǎn)拗口,我們可以想象生活的例子:現(xiàn)在我想在茶杯上能過(guò)濾出石頭和茶葉出來(lái)。石頭在一層,茶葉在一層。所以茶杯的過(guò)濾裝置應(yīng)該有兩層濾網(wǎng)。這個(gè)過(guò)濾裝置就是FilterChain,過(guò)濾石頭的濾網(wǎng)和過(guò)濾茶葉的濾網(wǎng)就是Filter。在石頭濾網(wǎng)中,茶葉是屬于下一層的,就把茶葉放行,讓茶葉的濾網(wǎng)過(guò)濾茶葉。過(guò)濾完茶葉了,剩下的就是茶(茶就可以比喻成我們的目標(biāo)資源)
快速入門 寫一個(gè)簡(jiǎn)單的過(guò)濾器實(shí)現(xiàn)Filter接口的Java類就被稱作為過(guò)濾器
public class FilterDemo1 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //執(zhí)行這一句,說(shuō)明放行(讓下一個(gè)過(guò)濾器執(zhí)行,如果沒(méi)有過(guò)濾器了,就執(zhí)行執(zhí)行目標(biāo)資源) chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { } }filter部署
過(guò)濾器和Servlet是一樣的,需要部署到Web服務(wù)器上的。
第一種方式:在web.xml文件中配置 filterFilterDemo1 FilterDemo1 word_file /WEB-INF/word.txt
一個(gè)Filter攔截的資源可通過(guò)兩種方式來(lái)指定:Servlet 名稱和資源訪問(wèn)的請(qǐng)求路徑
FilterDemo1 /*
REQUEST:當(dāng)用戶直接訪問(wèn)頁(yè)面時(shí),Web容器將會(huì)調(diào)用過(guò)濾器。如果目標(biāo)資源是通過(guò)RequestDispatcher的include()或forward()方法訪問(wèn)時(shí),那么該過(guò)濾器就不會(huì)被調(diào)用。
INCLUDE:如果目標(biāo)資源是通過(guò)RequestDispatcher的include()方法訪問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用。除此之外,該過(guò)濾器不會(huì)被調(diào)用。
FORWARD:如果目標(biāo)資源是通過(guò)RequestDispatcher的forward()方法訪問(wèn)時(shí),那么該過(guò)濾器將被調(diào)用,除此之外,該過(guò)濾器不會(huì)被調(diào)用。
ERROR:如果目標(biāo)資源是通過(guò)聲明式異常處理機(jī)制調(diào)用時(shí),那么該過(guò)濾器將被調(diào)用。除此之外,過(guò)濾器不會(huì)被調(diào)用。
第二種方式:通過(guò)注解配置@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")
上面的配置是“/*”,所有的Web資源都需要途徑過(guò)濾器
如果想要部分的Web資源進(jìn)行過(guò)濾器過(guò)濾則需要指定Web資源的名稱即可!
過(guò)濾器的執(zhí)行順序上面已經(jīng)說(shuō)過(guò)了,過(guò)濾器的doFilter()方法是極其重要的,FilterChain接口是代表著所有的Filter,F(xiàn)ilterChain中的doFilter()方法決定著是否放行下一個(gè)過(guò)濾器執(zhí)行(如果沒(méi)有過(guò)濾器了,就執(zhí)行目標(biāo)資源)。
測(cè)試一首先在過(guò)濾器的doFilter()中輸出一句話,并且調(diào)用chain對(duì)象的doFilter()方法
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("我是過(guò)濾器1"); //執(zhí)行這一句,說(shuō)明放行(讓下一個(gè)過(guò)濾器執(zhí)行,或者執(zhí)行目標(biāo)資源) chain.doFilter(req, resp); }
我們來(lái)訪問(wèn)一下test.jsp頁(yè)面:
我們發(fā)現(xiàn)test.jsp(我們的目標(biāo)資源)成功訪問(wèn)到了,并且在服務(wù)器上也打印了字符串!
測(cè)試二我們來(lái)試試把chain.doFilter(req, resp);這段代碼注釋了看看!
test.jsp頁(yè)面并沒(méi)有任何的輸出(也就是說(shuō),并沒(méi)有訪問(wèn)到j(luò)sp頁(yè)面)。
測(cè)試三直接看下面的代碼。我們已經(jīng)知道了”準(zhǔn)備放行“會(huì)被打印在控制臺(tái)上和test.jsp頁(yè)面也能被訪問(wèn)得到,但“放行完成“會(huì)不會(huì)打印在控制臺(tái)上呢?
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("準(zhǔn)備放行"); //執(zhí)行這一句,說(shuō)明放行(讓下一個(gè)過(guò)濾器執(zhí)行,或者執(zhí)行目標(biāo)資源) chain.doFilter(req, resp); System.out.println("放行完成"); }
答案也非常簡(jiǎn)單,肯定會(huì)打印在控制臺(tái)上的。我們來(lái)看看:
注意,它的完整流程順序是這樣的:客戶端發(fā)送http請(qǐng)求到Web服務(wù)器上,Web服務(wù)器執(zhí)行過(guò)濾器,執(zhí)行到”準(zhǔn)備放行“時(shí),就把字符串輸出到控制臺(tái)上,接著執(zhí)行doFilter()方法,Web服務(wù)器發(fā)現(xiàn)沒(méi)有過(guò)濾器了,就執(zhí)行目標(biāo)資源(也就是test.jsp)。目標(biāo)資源執(zhí)行完后,回到過(guò)濾器上,繼續(xù)執(zhí)行代碼,然后輸出”放行完成“
測(cè)試四我們?cè)俣嗉右粋€(gè)過(guò)濾器,看看執(zhí)行順序。
過(guò)濾器1
System.out.println("過(guò)濾器1開(kāi)始執(zhí)行"); //執(zhí)行這一句,說(shuō)明放行(讓下一個(gè)過(guò)濾器執(zhí)行,或者執(zhí)行目標(biāo)資源) chain.doFilter(req, resp); System.out.println("過(guò)濾器1開(kāi)始完畢");
過(guò)濾器2
System.out.println("過(guò)濾器2開(kāi)始執(zhí)行"); chain.doFilter(req, resp); System.out.println("過(guò)濾器2開(kāi)始完畢");
Servlet
System.out.println("我是Servlet1");
當(dāng)我們?cè)L問(wèn)Servlet1的時(shí)候,看看控制臺(tái)會(huì)出現(xiàn)什么:
執(zhí)行順序是這樣的:先執(zhí)行FilterDemo1,放行,執(zhí)行FilterDemo2,放行,執(zhí)行Servlet1,Servlet1執(zhí)行完回到FilterDemo2上,F(xiàn)ilterDemo2執(zhí)行完畢后,回到FilterDemo1上
注意:過(guò)濾器之間的執(zhí)行順序看在web.xml文件中mapping的先后順序的,如果放在前面就先執(zhí)行,放在后面就后執(zhí)行!如果是通過(guò)注解的方式配置,就比較urlPatterns的字符串優(yōu)先級(jí)
Filter簡(jiǎn)單應(yīng)用
filter的三種典型應(yīng)用:
1、可以在filter中根據(jù)條件決定是否調(diào)用chain.doFilter(request, response)方法,即是否讓目標(biāo)資源執(zhí)行
2、在讓目標(biāo)資源執(zhí)行之前,可以對(duì)requestresponse作預(yù)處理,再讓目標(biāo)資源執(zhí)行
3、在目標(biāo)資源執(zhí)行之后,可以捕獲目標(biāo)資源的執(zhí)行結(jié)果,從而實(shí)現(xiàn)一些特殊的功能
禁止瀏覽器緩存所有動(dòng)態(tài)頁(yè)面public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //讓W(xué)eb資源不緩存,很簡(jiǎn)單,設(shè)置http中response的請(qǐng)求頭即可了! //我們使用的是http協(xié)議,ServletResponse并沒(méi)有能夠設(shè)置請(qǐng)求頭的方法,所以要強(qiáng)轉(zhuǎn)成HttpServletRequest //一般我們寫Filter都會(huì)把他倆強(qiáng)轉(zhuǎn)成Http類型的 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setDateHeader("Expires", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //放行目標(biāo)資源的response已經(jīng)設(shè)置成不緩存的了 chain.doFilter(request, response); }
沒(méi)有過(guò)濾之前,響應(yīng)頭是這樣的:
過(guò)濾之后,響應(yīng)頭是這樣的:
實(shí)現(xiàn)自動(dòng)登陸 開(kāi)發(fā)實(shí)體、集合模擬數(shù)據(jù)庫(kù)、Dao實(shí)體:
private String username ; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } //各種setter和getter
集合模擬數(shù)據(jù)庫(kù)
public class UserDB { private static Listusers = new ArrayList<>(); static { users.add(new User("aaa", "123")); users.add(new User("bbb", "123")); users.add(new User("ccc", "123")); } public static List getUsers() { return users; } public static void setUsers(List users) { UserDB.users = users; } }
開(kāi)發(fā)dao
public User find(String username, String password) { List登陸界面userList = UserDB.getUsers(); //遍歷List集合,看看有沒(méi)有對(duì)應(yīng)的username和password for (User user : userList) { if (user.getUsername().equals(username) && user.getPassword().equals(password)) { return user; } } return null; }
處理登陸的Servlet
//得到客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù) String username = request.getParameter("username"); String password = request.getParameter("password"); UserDao userDao = new UserDao(); User user = userDao.find(username, password); if (user == null) { request.setAttribute("message", "用戶名或密碼是錯(cuò)的!"); request.getRequestDispatcher("/message.jsp").forward(request, response); } //如果不是為空,那么在session中保存一個(gè)屬性 request.getSession().setAttribute("user", user); request.setAttribute("message", "恭喜你,已經(jīng)登陸了!"); //如果想要用戶關(guān)閉了瀏覽器,還能登陸,就必須要用到Cookie技術(shù)了 Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + user.getPassword()); //設(shè)置Cookie的最大聲明周期為用戶指定的 cookie.setMaxAge(Integer.parseInt(request.getParameter("time")) * 60); //把Cookie返回給瀏覽器 response.addCookie(cookie); //跳轉(zhuǎn)到提示頁(yè)面 request.getRequestDispatcher("/message.jsp").forward(request, response);過(guò)濾器
HttpServletResponse response = (HttpServletResponse) resp; HttpServletRequest request = (HttpServletRequest) req; //如果用戶沒(méi)有關(guān)閉瀏覽器,就不需要Cookie做拼接登陸了 if (request.getSession().getAttribute("user") != null) { chain.doFilter(request, response); return; } //用戶關(guān)閉了瀏覽器,session的值就獲取不到了。所以要通過(guò)Cookie來(lái)自動(dòng)登陸 Cookie[] cookies = request.getCookies(); String value = null; for (int i = 0; cookies != null && i < cookies.length; i++) { if (cookies[i].getName().equals("autoLogin")) { value = cookies[i].getValue(); } } //得到Cookie的用戶名和密碼 if (value != null) { String username = value.split(".")[0]; String password = value.split(".")[1]; UserDao userDao = new UserDao(); User user = userDao.find(username, password); if (user != null) { request.getSession().setAttribute("user", user); } } chain.doFilter(request, response);
效果:
改良我們直接把用戶名和密碼都放在了Cookie中,這是明文的。懂點(diǎn)編程的人就會(huì)知道你的賬號(hào)了。
于是乎,我們要對(duì)密碼進(jìn)行加密!
Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + md5.md5(user.getPassword()));
在過(guò)濾器中,加密后的密碼就不是數(shù)據(jù)庫(kù)中的密碼的。所以,我們得在Dao添加一個(gè)功能【根據(jù)用戶名,找到用戶】
public User find(String username) { ListuserList = UserDB.getUsers(); //遍歷List集合,看看有沒(méi)有對(duì)應(yīng)的username和password for (User user : userList) { if (user.getUsername().equals(username)) { return user; } } return null; }
在過(guò)濾器中,比較Cookie帶過(guò)來(lái)的md5密碼和在數(shù)據(jù)庫(kù)中獲得的密碼(也經(jīng)過(guò)md5)是否相同
//得到Cookie的用戶名和密碼 if (value != null) { String username = value.split(".")[0]; String password = value.split(".")[1]; //在Cookie拿到的密碼是md5加密過(guò)的,不能直接與數(shù)據(jù)庫(kù)中的密碼比較 UserDao userDao = new UserDao(); User user = userDao.find(username); //通過(guò)用戶名獲得用戶信息,得到用戶的密碼,用戶的密碼也md5一把 String dbPassword = md5.md5(user.getPassword()); //如果兩個(gè)密碼匹配了,就是正確的密碼了 if (password.equals(dbPassword)) { request.getSession().setAttribute("user", user); } }
如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章的同學(xué),可以關(guān)注微信公眾號(hào):Java3y
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70947.html
摘要:采用完全獨(dú)立于任何程序語(yǔ)言的文本格式,使成為理想的數(shù)據(jù)交換語(yǔ)言為什么需要提到,我們就應(yīng)該和來(lái)進(jìn)行對(duì)比。也是一種存儲(chǔ)和交換文本信息的手段。那么好在哪里呢比更小更快,更易解析。使用的時(shí)候,也支持將轉(zhuǎn)成但是,我們不一定使用框架來(lái)做開(kāi)發(fā)呀。 什么是JSON JSON:JavaScript Object Notation 【JavaScript 對(duì)象表示法】 JSON 是存儲(chǔ)和交換文本信息的語(yǔ)法...
摘要:目錄前言架構(gòu)安裝第一個(gè)爬蟲爬取有道翻譯創(chuàng)建項(xiàng)目創(chuàng)建創(chuàng)建解析運(yùn)行爬蟲爬取單詞釋義下載單詞語(yǔ)音文件前言學(xué)習(xí)有一段時(shí)間了,當(dāng)時(shí)想要獲取一下百度漢字的解析,又不想一個(gè)個(gè)漢字去搜,復(fù)制粘貼太費(fèi)勁,考慮到爬蟲的便利性,這篇文章是介紹一個(gè)爬蟲框架, 目錄 前言 架構(gòu) 安裝 第一個(gè)爬蟲:爬取有道翻譯 創(chuàng)建項(xiàng)目 創(chuàng)建Item 創(chuàng)建Spider 解析 運(yùn)行爬蟲-爬取單詞釋義 下載單詞語(yǔ)音文件 ...
摘要:相信很多人在格式化字符串的時(shí)候都用的語(yǔ)法,提出一種更先進(jìn)的格式化方法并成為的標(biāo)準(zhǔn)用來(lái)替換舊的格式化語(yǔ)法,從開(kāi)始已經(jīng)實(shí)現(xiàn)了這一方法其它解釋器未考證。 showImg(https://segmentfault.com/img/remote/1460000018650325); 相信很多人在格式化字符串的時(shí)候都用%s % v的語(yǔ)法,PEP 3101 提出一種更先進(jìn)的格式化方法 str.for...
摘要:前言由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時(shí)間才會(huì)更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號(hào):Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡(jiǎn)單 注解就這么簡(jiǎn)單 Druid數(shù)據(jù)庫(kù)連接池...
閱讀 2311·2021-10-11 10:59
閱讀 2602·2021-10-11 10:58
閱讀 3304·2021-09-08 09:35
閱讀 3783·2021-09-02 15:21
閱讀 1455·2019-08-30 15:53
閱讀 2608·2019-08-29 14:16
閱讀 2068·2019-08-26 14:00
閱讀 2942·2019-08-26 13:52