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