国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

過濾器第一篇【介紹、入門、簡單應用】

2019-11-06 06:43:54
字體:
來源:轉載
供稿:網友

什么是過濾器

過濾器是Servlet的高級特性之一,也別把它想得那么高深,只不過是實現Filter接口的java類罷了!

首先,我們來看看過濾器究竟Web容器的哪處:

從上面的圖我們可以發現,當瀏覽器發送請求給服務器的時候,先執行過濾器,然后才訪問Web的資源。服務器響應Response,從Web資源抵達瀏覽器之前,也會途徑過濾器。

我們很容易發現,過濾器可以比喻成一張濾網。我們想想現實中的濾網可以做什么:在泡茶的時候,過濾掉茶葉。那濾網是怎么過濾茶葉的呢?規定大小的網孔,只要網孔比茶葉小,就可以實現過濾了!

引申在Web容器中,過濾器可以做:過濾一些敏感的字符串【規定不能出現敏感字符串】、避免中文亂碼【規定Web資源都使用UTF-8編碼】、權限驗證【規定只有帶session或Cookie的瀏覽器,才能訪問web資源】等等等,過濾器的作用非常大,只要發揮想象就可以有意想不到的效果

也就是說:當需要限制用戶訪問某些資源時、在處理請求時提前處理某些資源、服務器響應的內容對其進行處理再返回、我們就是用過濾器來完成的!


為什么需要用到過濾器

直接舉例子來說明吧:

沒有過濾器解決中文亂碼問題

如果我沒有用到過濾器:瀏覽器通過http請求發送數據給Servlet,如果存在中文,就必須指定編碼,否則就會亂碼!

jsp頁面提交中文數據給Servlet處理

<form action="${pageContext.request.contextPath}/Demo1" method="post"> <input type="text" name="username"> <input type="submit" value="提交"></form>Servlet沒有指定編碼的情況下,獲取得到的是亂碼

Servlet中如何解決中文亂碼問題,我的其他博文中有:http://blog.csdn.net/hon_3y/article/details/54632004

也就是說:如果我每次接受客戶端帶過來的中文數據,在Serlvet中都要設定編碼。這樣代碼的重復率太高了!!!!


有過濾器解決中文亂碼問題

有過濾器的情況就不一樣了:只要我在過濾器中指定了編碼,可以使全站的Web資源都是使用該編碼,并且重用性是非常理想的!


過濾器 API

只要Java類實現了Filter接口就可以稱為過濾器!Filter接口的方法也十分簡單:

其中init()和destory()方法就不用多說了,他倆跟Servlet是一樣的。只有在Web服務器加載和銷毀的時候被執行,只會被執行一次!

值得注意的是doFilter()方法,它有三個參數(ServletRequest,ServletResponse,FilterChain),從前兩個參數我們可以發現:過濾器可以完成任何協議的過濾操作

那FilterChain是什么東西呢?我們看看:

FilterChain是一個接口,里面又定義了doFilter()方法。這究竟是怎么回事啊??????

我們可以這樣理解:過濾器不單單只有一個,那么我們怎么管理這些過濾器呢?在Java中就使用了鏈式結構把所有的過濾器都放在FilterChain里邊,如果符合條件,就執行下一個過濾器(如果沒有過濾器了,就執行目標資源)

上面的話好像有點拗口,我們可以想象生活的例子:現在我想在茶杯上能過濾出石頭和茶葉出來。石頭在一層,茶葉在一層。所以茶杯的過濾裝置應該有兩層濾網。這個過濾裝置就是FilterChain,過濾石頭的濾網和過濾茶葉的濾網就是Filter。在石頭濾網中,茶葉是屬于下一層的,就把茶葉放行,讓茶葉的濾網過濾茶葉。過濾完茶葉了,剩下的就是茶(茶就可以比喻成我們的目標資源)


快速入門

寫一個簡單的過濾器

實現Filter接口的Java類就被稱作為過濾器 public class FilterDemo1 implements Filter { public void destroy() { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //執行這一句,說明放行(讓下一個過濾器執行,如果沒有過濾器了,就執行執行目標資源) chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException { } }

filter部署

過濾器和Servlet是一樣的,需要部署到Web服務器上的。

第一種方式:在web.xml文件中配置

filter

<filter>用于注冊過濾器

<filter> <filter-name>FilterDemo1</filter-name> <filter-class>FilterDemo1</filter-class> <init-param> <param-name>Word_file</param-name> <param-value>/WEB-INF/word.txt</param-value> </init-param> </filter><filter-name>用于為過濾器指定一個名字,該元素的內容不能為空。<filter-class>元素用于指定過濾器的完整的限定類名<init-param>元素用于為過濾器指定初始化參數,它的子元素指定參數的名字,<param-value>指定參數的值。在過濾器中,可以使用FilterConfig接口對象來訪問初始化參數

filter-mapping

<filter-mapping>元素用于設置一個Filter 所負責攔截的資源

一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑

<filter-mapping> <filter-name>FilterDemo1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping><filter-name>子元素用于設置filter的注冊名稱。該值必須是在元素中聲明過的過濾器的名字<url-pattern>設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)<servlet-name>指定過濾器所攔截的Servlet名稱<dispatcher>指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默認REQUEST。用戶可以設置多個<dispatcher> 子元素用來指定 Filter 對資源的多種調用方式進行攔截。

dispatcher

子元素可以設置的值及其意義: - REQUEST:當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那么該過濾器就不會被調用。 - INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那么該過濾器將被調用。除此之外,該過濾器不會被調用。 - FORWARD:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那么該過濾器將被調用,除此之外,該過濾器不會被調用。 - ERROR:如果目標資源是通過聲明式異常處理機制調用時,那么該過濾器將被調用。除此之外,過濾器不會被調用。

第二種方式:通過注解配置

@WebFilter(filterName = "FilterDemo1",urlPatterns = "/*")

上面的配置是“/*”,所有的Web資源都需要途徑過濾器

如果想要部分的Web資源進行過濾器過濾則需要指定Web資源的名稱即可!


過濾器的執行順序

上面已經說過了,過濾器的doFilter()方法是極其重要的,FilterChain接口是代表著所有的Filter,FilterChain中的doFilter()方法決定著是否放行下一個過濾器執行(如果沒有過濾器了,就執行目標資源)

測試一

首先在過濾器的doFilter()中輸出一句話,并且調用chain對象的doFilter()方法 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.

我們發現test.jsp(我們的目標資源)成功訪問到了,并且在服務器上也打印了字符串!


測試二

我們來試試把chain.doFilter(req, resp);這段代碼注釋了看看!

test.jsp頁面并沒有任何的輸出(也就是說,并沒有訪問到jsp頁面)。


測試三

直接看下面的代碼。我們已經知道了”準備放行“會被打印在控制臺上和test.jsp頁面也能被訪問得到,但“放行完成“會不會打印在控制臺上呢?

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { System.out.println("準備放行"); //執行這一句,說明放行(讓下一個過濾器執行,或者執行目標資源) chain.doFilter(req, resp); System.out.println("放行完成"); }

答案也非常簡單,肯定會打印在控制臺上的。我們來看看:

注意,它的完整流程順序是這樣的:客戶端發送http請求到Web服務器上,Web服務器執行過濾器,執行到”準備放行“時,就把字符串輸出到控制臺上,接著執行doFilter()方法,Web服務器發現沒有過濾器了,就執行目標資源(也就是test.jsp)。目標資源執行完后,回到過濾器上,繼續執行代碼,然后輸出”放行完成“

測試四

我們再多加一個過濾器,看看執行順序。

過濾器1 System.out.println("過濾器1開始執行"); //執行這一句,說明放行(讓下一個過濾器執行,或者執行目標資源) chain.doFilter(req, resp); System.out.println("過濾器1開始完畢");過濾器2 System.out.println("過濾器2開始執行"); chain.doFilter(req, resp); System.out.println("過濾器2開始完畢");Servlet System.out.println("我是Servlet1");

當我們訪問Servlet1的時候,看看控制臺會出現什么:

執行順序是這樣的:先執行FilterDemo1,放行,執行FilterDemo2,放行,執行Servlet1,Servlet1執行完回到FilterDemo2上,FilterDemo2執行完畢后,回到FilterDemo1上


注意:過濾器之間的執行順序看在web.xml文件中mapping的先后順序的,如果放在前面就先執行,放在后面就后執行!如果是通過注解的方式配置,就比較urlPatterns的字符串大小


Filter簡單應用

filter的三種典型應用: 1、可以在filter中根據條件決定是否調用chain.doFilter(request, response)方法,即是否讓目標資源執行2、在讓目標資源執行之前,可以對request/response作預處理,再讓目標資源執行3、在目標資源執行之后,可以捕獲目標資源的執行結果,從而實現一些特殊的功能

禁止瀏覽器緩存所有動態頁面

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //讓Web資源不緩存,很簡單,設置http中response的請求頭即可了! //我們使用的是http協議,ServletResponse并沒有能夠設置請求頭的方法,所以要強轉成HttpServletRequest //一般我們寫Filter都會把他倆強轉成Http類型的 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setDateHeader("Expires", -1); response.setHeader("Cache-Control", "no-cache"); response.setHeader("Pragma", "no-cache"); //放行目標資源的response已經設置成不緩存的了 chain.doFilter(request, response); }沒有過濾之前,響應頭是這樣的:

過濾之后,響應頭是這樣的:


實現自動登陸

開發實體、集合模擬數據庫、Dao

實體: private String username ; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } //各種setter和getter集合模擬數據庫 public class UserDB { private static List<User> users = new ArrayList<>(); static { users.add(new User("aaa", "123")); users.add(new User("bbb", "123")); users.add(new User("ccc", "123")); } public static List<User> getUsers() { return users; } public static void setUsers(List<User> users) { UserDB.users = users; } }開發dao public User find(String username, String password) { List<User> userList = UserDB.getUsers(); //遍歷List集合,看看有沒有對應的username和password for (User user : userList) { if (user.getUsername().equals(username) && user.getPassword().equals(password)) { return user; } } return null; }

登陸界面

<form action="${pageContext.request.contextPath}/LoginServlet"> 用戶名<input type="text" name="username"> <br> 密碼<input type="password" name="password"> <br> <input type="radio" name="time" value="10">10分鐘 <input type="radio" name="time" value="30">30分鐘 <input type="radio" name="time" value="60">1小時 <br> <input type="submit" value="登陸"></form>

處理登陸的Servlet

//得到客戶端發送過來的數據 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", "恭喜你,已經登陸了!"); //如果想要用戶關閉了瀏覽器,還能登陸,就必須要用到Cookie技術了 Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + user.getPassword()); //設置Cookie的最大聲明周期為用戶指定的 cookie.setMaxAge(Integer.parseInt(request.getParameter("time")) * 60); //把Cookie返回給瀏覽器 response.addCookie(cookie); //跳轉到提示頁面 request.getRequestDispatcher("/message.jsp").forward(request, response);

過濾器

HttpServletResponse response = (HttpServletResponse) resp; HttpServletRequest request = (HttpServletRequest) req; //如果用戶沒有關閉瀏覽器,就不需要Cookie做拼接登陸了 if (request.getSession().getAttribute("user") != null) { chain.doFilter(request, response); return; } //用戶關閉了瀏覽器,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("http://.")[0]; String password = value.split("http://.")[1]; UserDao userDao = new UserDao(); User user = userDao.find(username, password); if (user != null) { request.getSession().setAttribute("user", user); } } chain.doFilter(request, response);效果:


改良

我們直接把用戶名和密碼都放在了Cookie中,這是明文的。懂點編程的人就會知道你的賬號了。

于是乎,我們要對密碼進行加密!

Cookie cookie = new Cookie("autoLogin", user.getUsername() + "." + md5.md5(user.getPassword()));在過濾器中,加密后的密碼就不是數據庫中的密碼的。所以,我們得在Dao添加一個功能【根據用戶名,找到用戶】 public User find(String username) { List<User> userList = UserDB.getUsers(); //遍歷List集合,看看有沒有對應的username和password for (User user : userList) { if (user.getUsername().equals(username)) { return user; } } return null; }在過濾器中,比較Cookie帶過來的md5密碼和在數據庫中獲得的密碼(也經過md5)是否相同 //得到Cookie的用戶名和密碼 if (value != null) { String username = value.split("http://.")[0]; String password = value.split("http://.")[1]; //在Cookie拿到的密碼是md5加密過的,不能直接與數據庫中的密碼比較 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); } }

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 汉川市| 南宁市| 水城县| 临沂市| 德庆县| 喀喇沁旗| 龙泉市| 南陵县| 芷江| 昭平县| 白玉县| 阳江市| 滨海县| 柏乡县| 康马县| 渝北区| 唐海县| 安塞县| 自贡市| 金门县| 威海市| 天峻县| 玉龙| 广昌县| 宁乡县| 内江市| 宜州市| 清新县| 乃东县| 三门县| 宁津县| 子长县| 揭东县| 邹平县| 旌德县| 平遥县| 绥江县| 栾川县| 东方市| 绥阳县| 蕲春县|