• JavaWeb——过滤器


    过滤器简介

      WEB过滤器是一个服务器端的组件,它可以截取用户端的请求与相应信息,并对这些信息过滤。

    过滤器的工作原理和生命周期

      在没有Web过滤器的情况下,用户直接访问服务器上的Web资源。但是如果存在过滤器,用户就不可以直接访问过滤器了。

      Web容器启动的时候过滤器就已经启动了,用户的请求到达过滤器,过滤器判断用户的请求是否符合过滤规则,如果符合规则则将用户的请求发送给Web资源,Web资源将响应信息发送给过滤器,过滤器将Web资源的响应发送给用户。工作原理如下图所示:

      过滤器的生命周期:

      其中实例化方法在Web容器开始装载的时候就执行,初始化方法配置一些初始化参数,Web容器卸载(服务器关闭)的时候执行销毁方法。过滤方法会执行多次,其他方法只会执行一次。

    第一个过滤器

        1.创建一个类实现javax.servlet.Filter接口。需要实现该接口中的3个方法。

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 
    12 public class FirstFilter implements Filter {
    13 
    14     public void destroy() {
    15         System.out.println("*********执行过滤器的销毁方法*******");
    16 
    17     }
    18 
    19     public void doFilter(ServletRequest request, ServletResponse response,
    20             FilterChain filterChain) throws IOException, ServletException {
    21         System.out.println("*******开始执行过滤器的doFilter方法*********");
    22         filterChain.doFilter(request, response);
    23         System.out.println("*******结束执行过滤器的doFilter方法**********");
    24     }
    25 
    26     public void init(FilterConfig arg0) throws ServletException {
    27         System.out.println("*******执行过滤器的初始化方法***********");
    28 
    29     }
    30 
    31 }
    FirstFilter.java
    • init(FilterConfig ):过滤器的初始化方法,Web容器创建过滤器之后将调用这个方法,在这个方法中可以读取web.xml中的过滤器参数。
    • doFilter(ServletRequest,ServletResponse,FilterChain):完成实际的过滤操作,是过滤器的核心方法。当用户请求访问与过滤器相关联的URL的时候,Web容器将先调用过滤器的doFilter方法。FilterChain参数可以调用chain.doFilter方法【放行方法】,将请求传送给下一个过滤器(或者目标资源),或利用转发、重定向将请求转发给其他资源。
    • destroy():Web容器在销毁过滤器实例前调用该方法,在这个方法中可以释放过滤器占用的资源。【大多数情况下用不到】

        2.在web.xml中配置过滤器(和再web.xml中注册servlet类似)

      在MyEclipse中提供了可视化的配置web.xml,如下图:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app version="2.5" 
     3     xmlns="http://java.sun.com/xml/ns/javaee" 
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     6     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     7   <display-name></display-name>    
     8   <welcome-file-list>
     9     <welcome-file>index.jsp</welcome-file>
    10   </welcome-file-list>
    11   
    12       <!-- 配置过滤器开始 -->
    13     <filter>
    14         <filter-name>FirstFilter</filter-name>
    15         <filter-class>filter.FirstFilter</filter-class>
    16     </filter>
    17     <filter-mapping>
    18         <filter-name>FirstFilter</filter-name>
    19         <url-pattern>/*</url-pattern>
    20     </filter-mapping>
    21     <!-- 配置过滤器结束 -->
    22     
    23 </web-app>
    web.xml

      在index.jsp中我们在控制台打印一行信息:"**********这是index.jsp*********";   

      将项目部署到服务器,启动服务器:

      在Servlet容器装载的时候,执行过滤器的init方法,当用户请求页面的时候首先执行doFilter()方法,当Servlet容器卸载的时候执行过滤器的销毁方法。【注意:用户的请求先是到达过滤器并不是直接访问的Web资源】

      还有一点需要注意:用户虽然能够改变用户请求的资源(例如:网上购物的时候点击“立即购买”,这个请求先要到达过滤器,如果过滤器检测到用户没有登录,就会将页面重定向到登陆页),但是过滤器不能直接处理用户的请求(过滤器不是Servlet),不能直接返回数据。

    过滤器链

      针对同一个用户请求(url-pattern),与之匹配的过滤器有多个,这个时候用户请求就会依次通过各个过滤器到达web资源。

      新建2个过滤器:

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 
    12 public class FirstFilter implements Filter {
    13 
    14     public void destroy() {
    15         System.out.println("*********过滤器1----->销毁方法*******");
    16 
    17     }
    18 
    19     public void doFilter(ServletRequest request, ServletResponse response,
    20             FilterChain filterChain) throws IOException, ServletException {
    21         System.out.println("*******开始执行过滤器1----->doFilter方法*********");
    22         filterChain.doFilter(request, response);
    23         System.out.println("*******结束执行过滤器1----->doFilter方法**********");
    24     }
    25 
    26     public void init(FilterConfig arg0) throws ServletException {
    27         System.out.println("*******执行过滤器1------>初始化方法***********");
    28 
    29     }
    30 
    31 }
    FirstFilter.java
     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 
    12 public class SecondFilter implements Filter {
    13 
    14     public void destroy() {
    15         System.out.println("*********过滤器2----->销毁方法*******");
    16     }
    17 
    18     public void doFilter(ServletRequest request, ServletResponse response,
    19             FilterChain filterChain) throws IOException, ServletException {
    20         System.out.println("*******开始执行过滤器2----->doFilter方法*********");
    21         filterChain.doFilter(request, response);
    22         System.out.println("*******结束执行过滤器2----->doFilter方法**********");
    23     }
    24 
    25     public void init(FilterConfig arg0) throws ServletException {
    26         System.out.println("*******执行过滤器2------>初始化方法***********");
    27     }
    28 
    29 }
    SecondFilter.java

      在web.xml中配置两个过滤器(第一个过滤器在前面,两个过滤器都匹配index.jsp)

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app version="2.5" 
     3     xmlns="http://java.sun.com/xml/ns/javaee" 
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     6     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     7   <display-name></display-name>    
     8   <welcome-file-list>
     9     <welcome-file>index.jsp</welcome-file>
    10   </welcome-file-list>
    11   
    12       <!-- 配置过滤器开始 -->
    13       <!-- 过滤器1 -->
    14     <filter>
    15         <filter-name>FirstFilter</filter-name>
    16         <filter-class>filter.FirstFilter</filter-class>
    17     </filter>
    18     <filter-mapping>
    19         <filter-name>FirstFilter</filter-name>
    20         <url-pattern>/index.jsp</url-pattern>
    21     </filter-mapping>
    22     <!-- 过滤器2 -->    
    23     <filter>
    24         <filter-name>SecondFilter</filter-name>
    25         <filter-class>filter.SecondFilter</filter-class>
    26     </filter>
    27     <filter-mapping>
    28         <filter-name>SecondFilter</filter-name>
    29         <url-pattern>/index.jsp</url-pattern>
    30     </filter-mapping>
    31    <!-- 配置过滤器结束 -->
    32     
    33 </web-app>
    web.xml

      在index.jsp中我们打印如下的一句话: System.out.println("******** 这是index.jsp,处理过程完成 *********"); 

    运行结果:

    过滤器的分类:

      Servlet 2.5中将过滤器分为4种,如下图所示:

    1.REQUEST过滤器

      新建一个过滤器FirstFilter(在web.xml中配置两个过滤地址index.jsp和main.jsp): 

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app version="2.5" 
     3     xmlns="http://java.sun.com/xml/ns/javaee" 
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     6     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     7   <display-name></display-name>    
     8   <welcome-file-list>
     9     <welcome-file>index.jsp</welcome-file>
    10   </welcome-file-list>
    11   
    12       <!-- 配置过滤器开始 -->
    13     <filter>
    14         <filter-name>FirstFilter</filter-name>
    15         <filter-class>filter.FirstFilter</filter-class>
    16     </filter>
    17     <filter-mapping>
    18         <filter-name>FirstFilter</filter-name>
    19         <url-pattern>/index.jsp</url-pattern>
    20         <dispatcher>REQUEST</dispatcher>
    21     </filter-mapping>
    22     <filter-mapping>
    23         <filter-name>FirstFilter</filter-name>
    24         <url-pattern>/main.jsp</url-pattern>
    25         <dispatcher>REQUEST</dispatcher>
    26     </filter-mapping>
    27    <!-- 配置过滤器结束 -->
    28     
    29 </web-app>
    web.xml

      在FirstFilter的doFilter方法中将页面重定向到main.jsp

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 import javax.servlet.http.HttpServletRequest;
    12 import javax.servlet.http.HttpServletResponse;
    13 
    14 public class FirstFilter implements Filter {
    15 
    16     public void destroy() {
    17         System.out.println("*********过滤器1----->销毁方法*******");
    18 
    19     }
    20 
    21     public void doFilter(ServletRequest request, ServletResponse response,
    22             FilterChain filterChain) throws IOException, ServletException {
    23         
    24         HttpServletRequest req = (HttpServletRequest) request;
    25         HttpServletResponse res = (HttpServletResponse) response;
    26         
    27         System.out.println("*******开始执行过滤器1----->doFilter方法*********");
    28         res.sendRedirect(req.getContextPath() + "/main.jsp");//重定向
    29         System.out.println("*******结束执行过滤器1----->doFilter方法**********");
    30     }
    31 
    32     public void init(FilterConfig arg0) throws ServletException {
    33         System.out.println("*******执行过滤器1------>初始化方法***********");
    34 
    35     }
    36 
    37 }
    FirstFilter.java

      启动服务器:

      当我们访问index.jsp的时候,过滤器将页面重定向到main.jsp——相当于我们又重新请求了main.jsp,main.jsp又会遇到过滤器,如此页面不停重定向到main.jsp,产生了死循环不会输出任何内容!

      换一种方式,我们在过滤器中使用服务器内部转发的方式将页面转发到main.jsp(main.jsp向页面打印"这是main.jsp")。

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 import javax.servlet.http.HttpServletRequest;
    12 import javax.servlet.http.HttpServletResponse;
    13 
    14 public class FirstFilter implements Filter {
    15 
    16   public void destroy() {
    17     System.out.println("*********过滤器1----->销毁方法*******");
    18 
    19   }
    20 
    21   public void doFilter(ServletRequest request, ServletResponse response,
    22       FilterChain filterChain) throws IOException, ServletException {
    23     
    24     HttpServletRequest req = (HttpServletRequest) request;
    25     HttpServletResponse res = (HttpServletResponse) response;
    26     
    27     System.out.println("*******开始执行过滤器1----->doFilter方法*********");
    28     req.getRequestDispatcher("main.jsp").forward(request, response);//使用服务器内部转发
    29     System.out.println("*******结束执行过滤器1----->doFilter方法**********");
    30   }
    31 
    32   public void init(FilterConfig arg0) throws ServletException {
    33     System.out.println("*******执行过滤器1------>初始化方法***********");
    34 
    35   }
    36 
    37 }
    FirstFilter.java

      运行结果:

    2.FORWARD过滤器

      将main.jsp的过滤规则改为FORWARD。

    1 …… 
    2 <filter-mapping>
    3         <filter-name>FirstFilter</filter-name>
    4         <url-pattern>/main.jsp</url-pattern>
    5         <dispatcher>FORWARD</dispatcher>
    6  </filter-mapping>
    7 ……

      重新访问index.jsp

    3.INCLUDE过滤器

      INCLUDE和FORWARD过滤器的使用类似,对应的jsp动作是include。

    4.ERROR过滤器

      例如我们访问一个错误的页面时,系统会给出一个错误,用户看不懂这个错误是什么概念——我们需要给出一些人性化的提示。

      以下是没有在web.xml中配置错误页,用户访问一个不存在的页面:

      在web.xml中配置错误页:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app version="2.5" 
     3   xmlns="http://java.sun.com/xml/ns/javaee" 
     4   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     6   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     7   <display-name></display-name> 
     8   <welcome-file-list>
     9     <welcome-file>index.jsp</welcome-file>
    10   </welcome-file-list>
    11 
    12   <!-- 配置错误页面 -->
    13   <error-page>
    14     <error-code>404</error-code>
    15     <location>/error.jsp</location>
    16   </error-page>
    17 
    18 </web-app>
    web.xml

      在web.xml中配置404错误的页面为error.jsp则当用户访问一个不存在的页面时将会将error.jsp中的内容输出(以人性化的方式提示用户):

      当页面出现错误或者异常的时候,ERROR过滤器可以将错误或者异常捕捉到,系统会记录下错误的信息,程序员就可以查找错误的来源。

      新建一个ErrorFilter,并在web.xml中进行如下配置:

     1 <!-- 配置错误页面 -->
     2   <error-page>
     3     <error-code>404</error-code>
     4     <location>/error.jsp</location>
     5   </error-page>
     6 ……
     7 <filter>
     8     <filter-name>ErrorFilter</filter-name>
     9     <filter-class>filter.ErrorFilter</filter-class>
    10 </filter>
    11 <filter-mapping>
    12     <filter-name>ErrorFilter</filter-name>
    13     <url-pattern>/error.jsp</url-pattern>
    14     <dispatcher>ERROR</dispatcher>
    15 </filter-mapping>
    16 ……
    web.xml

      错误过滤器ErrorFilter

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.Filter;
     6 import javax.servlet.FilterChain;
     7 import javax.servlet.FilterConfig;
     8 import javax.servlet.ServletException;
     9 import javax.servlet.ServletRequest;
    10 import javax.servlet.ServletResponse;
    11 
    12 public class ErrorFilter implements Filter {
    13 
    14     public void destroy() {
    15 
    16     }
    17 
    18     public void doFilter(ServletRequest request, ServletResponse response,
    19             FilterChain filterChain) throws IOException, ServletException {
    20         System.out.println("检测到错误信息!");
    21         filterChain.doFilter(request, response);//注意打印错误后要放行,不然页面不会显示
    22     }
    23 
    24     public void init(FilterConfig filterConfig) throws ServletException {
    25 
    26     }
    27 
    28 }
    ErrorFilter.java

      

      J2EE5默认的web.xml是Servlet 2.5,J2EE6默认的web.xml是Servlet 3.0【支持异步处理】。

      异步处理:如果在过滤器的doFilter()方法中页面跳转到了一个Servlet用于业务的处理,加入Servlet处理的业务时间花费很多,这时过滤器就会一直等待Servlet执行完成。这样用户体验就会非常差。Servlet 3.0支持异步处理。

      在Servlet中新加入了@WebFilter Annotation。该Annotation用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置相应的类,将响应的类部署为过滤器。——因此我们不需要在web.xml中配置过滤器,只需要用注解的方式。@WebFilter的常用属性如下:

       下面使用J2EE6.0创建一个Web项目,创建一个过滤器名称为AsyncFilter,使用@WebFilter Annotation配置该过滤器(PS:可以不再web.xml中注册该过滤器):

     1 package filter;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.DispatcherType;
     6 import javax.servlet.Filter;
     7 import javax.servlet.FilterChain;
     8 import javax.servlet.FilterConfig;
     9 import javax.servlet.ServletException;
    10 import javax.servlet.ServletRequest;
    11 import javax.servlet.ServletResponse;
    12 import javax.servlet.annotation.WebFilter;
    13 
    14 @WebFilter(filterName = "AsyncFilter", value = { "/servlet/AsyncServlet" }, asyncSupported = true, dispatcherTypes = {
    15         DispatcherType.ASYNC, DispatcherType.REQUEST })
    16 public class AsyncFilter implements Filter {
    17 
    18     @Override
    19     public void destroy() {
    20         System.out.println("***** 销毁AsyncFilter *****");
    21     }
    22 
    23     @Override
    24     public void doFilter(ServletRequest request, ServletResponse response,
    25             FilterChain filterChain) throws IOException, ServletException {
    26         System.out.println("***** 开始AsyncFilter *****");
    27         filterChain.doFilter(request, response);//放行
    28         System.out.println("***** 结束AsyncFilter *****");
    29     }
    30 
    31     @Override
    32     public void init(FilterConfig filterConfig) throws ServletException {
    33         System.out.println("***** 初始化AsyncFilter *****");
    34     }
    35 
    36 }
    AsyncFilter.java

      创建一个处理业务的Servlet(该Servlet在doGet方法中使用了线程的休眠方法休眠了10s模拟业务的花费时间)

     1 package servlet;
     2 
     3 import java.io.IOException;
     4 import java.util.Date;
     5 
     6 import javax.servlet.AsyncContext;
     7 import javax.servlet.ServletException;
     8 import javax.servlet.http.HttpServlet;
     9 import javax.servlet.http.HttpServletRequest;
    10 import javax.servlet.http.HttpServletResponse;
    11 
    12 public class AsyncServlet extends HttpServlet {
    13     
    14     public class Excutor implements Runnable{
    15         private AsyncContext context;
    16         public Excutor(AsyncContext context) {
    17             this.context = context;
    18         }
    19 
    20         @Override
    21         public void run() {
    22             // 执行相关的复杂业务
    23             try {
    24                 Thread.sleep(1000*10);//休眠10s
    25 //                context.getRequest();
    26 //                context.getResponse();
    27                 System.out.println("业务完成执行时间:"+new Date());
    28             } catch (InterruptedException e) {
    29                 e.printStackTrace();
    30             }
    31         }
    32         
    33     }
    34 
    35     public AsyncServlet() {
    36         super();
    37     }
    38 
    39     public void destroy() {
    40         super.destroy(); // Just puts "destroy" string in log
    41     }
    42 
    43     public void doGet(HttpServletRequest request, HttpServletResponse response)
    44             throws ServletException, IOException {
    45         
    46         AsyncContext context = request.startAsync(); //HttpServletRequest对象开始异步方法
    47 //        context.getRequest();
    48 //        context.getResponse();
    49         
    50         System.out.println("Servlet执行开始时间:"+new Date());
    51         new Thread(new Excutor(context)).start();
    52         request.getRequestDispatcher("/index.jsp").forward(request, response);
    53         System.out.println("Servlet执行结束时间:"+new Date());
    54     }
    55 
    56     public void doPost(HttpServletRequest request, HttpServletResponse response)
    57             throws ServletException, IOException {
    58 
    59         doGet(request, response);
    60     }
    61 
    62     public void init() throws ServletException {
    63 
    64     }
    65 
    66 }
    AsyncServlet.java

      编写好异步处理业务的Servlet类之后一定要在web.xml中配置该Servlet启用异步,如下图所示:

       在index.jsp中有一个链接指向AsyncServlet:

    <a href="<%=request.getContextPath() %>/servlet/AsyncServlet">点击跳转到AsyncServlet处理业务</a>

      运行结果:

     

    过滤器在实际项目中的应用场景

    用户身份的验证

      例如现在有一个web项目,登陆页login.jsp将用户名和密码提交给LoginServlet处理,Servlet使用request.getParameter方法获得表单中的用户名和密码,将用户名和密码进行验证,验证成功则页面重定向到success.jsp,显示登陆成功和登录的用户名【保存在session中再从session中取出】;如果登录失败则重定向到failure.jsp。

      在不使用过滤器的情况下,即使用户不进行登录也可以访问到success.jsp——任何人都可以访问到success.jsp,这显然来说不安全。

      验证用户身份的Servlet(验证用户名和密码都是admin):

     1 package servlet;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.ServletException;
     6 import javax.servlet.http.HttpServlet;
     7 import javax.servlet.http.HttpServletRequest;
     8 import javax.servlet.http.HttpServletResponse;
     9 import javax.servlet.http.HttpSession;
    10 
    11 public class LoginServlet extends HttpServlet {
    12 
    13     public LoginServlet() {
    14         super();
    15     }
    16 
    17     public void destroy() {
    18         super.destroy(); // Just puts "destroy" string in log
    19     }
    20 
    21     public void doPost(HttpServletRequest request, HttpServletResponse response)
    22             throws ServletException, IOException {
    23         String username = request.getParameter("username");
    24         String password = request.getParameter("password");
    25         
    26         if ("admin".equals(username)&&"admin".equals(password)) {
    27             // 验证通过
    28             HttpSession session = request.getSession();
    29             session.setAttribute("username", username);//把用户名放入session中
    30             response.sendRedirect(request.getContextPath()+"/success.jsp");
    31         }else {
    32             // 验证失败
    33             response.sendRedirect(request.getContextPath()+"/failure.jsp");
    34         }
    35     }
    36 
    37     public void init() throws ServletException {
    38 
    39     }
    40 
    41 }
    LoginServlet.java

    如下图所示:

      现在我们要做这样一件事:只有用户登录成功才可以访问到success.jsp,反之跳转到登录界面。即:不允许未登录用户访问success.jsp。

      创建一个登录校验的过滤器LoginFilter并在web.xml中配置该过滤器的url为success.jsp

     1 package servlet;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.servlet.ServletException;
     6 import javax.servlet.http.HttpServlet;
     7 import javax.servlet.http.HttpServletRequest;
     8 import javax.servlet.http.HttpServletResponse;
     9 import javax.servlet.http.HttpSession;
    10 
    11 public class LoginServlet extends HttpServlet {
    12 
    13     public LoginServlet() {
    14         super();
    15     }
    16 
    17     public void destroy() {
    18         super.destroy(); // Just puts "destroy" string in log
    19     }
    20 
    21     public void doPost(HttpServletRequest request, HttpServletResponse response)
    22             throws ServletException, IOException {
    23         String username = request.getParameter("username");
    24         String password = request.getParameter("password");
    25         
    26         if ("admin".equals(username)&&"admin".equals(password)) {
    27             // 验证通过
    28             HttpSession session = request.getSession();
    29             session.setAttribute("username", username);//把用户名放入session中
    30             response.sendRedirect(request.getContextPath()+"/success.jsp");
    31         }else {
    32             // 验证失败
    33             response.sendRedirect(request.getContextPath()+"/failure.jsp");
    34         }
    35     }
    36 
    37     public void init() throws ServletException {
    38 
    39     }
    40 
    41 }
    LoginFilter.java
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app version="2.5" 
     3     xmlns="http://java.sun.com/xml/ns/javaee" 
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     6     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
     7   <display-name></display-name>
     8   <servlet>
     9     <description>This is the description of my J2EE component</description>
    10     <display-name>This is the display name of my J2EE component</display-name>
    11     <servlet-name>LoginServlet</servlet-name>
    12     <servlet-class>servlet.LoginServlet</servlet-class>
    13   </servlet>
    14 
    15   <servlet-mapping>
    16     <servlet-name>LoginServlet</servlet-name>
    17     <url-pattern>/servlet/LoginServlet</url-pattern>
    18   </servlet-mapping>    
    19   <welcome-file-list>
    20     <welcome-file>index.jsp</welcome-file>
    21   </welcome-file-list>
    22     <filter>
    23         <filter-name>LoginFilter</filter-name>
    24         <filter-class>filter.LoginFilter</filter-class>
    25     </filter>
    26     <filter-mapping>
    27         <filter-name>LoginFilter</filter-name>
    28         <url-pattern>/success.jsp</url-pattern>
    29     </filter-mapping>
    30 </web-app>
    web.xml

      加入过滤器之后(只有成功登录的用户【session中保存了该用户的用户名】才可以访问index.jsp):

     在实际的开发中一个网站的页面成百上千,这时我们可以使用通配符"/*"来匹配所有的url。但是这样带来一个问题:

     当我们新建立一个session的时候,访问任何页面都会被重定向到login.jsp,login.jsp自己也重定向到login.jsp,这样就形成了重定向循环

      这时就需要在过滤器中判断页面是否是login.jsp,如果是login.jsp或者请求的是LoginServlet就直接放行。在LoginFilter的doFilter方法中添加这样几行代码:

    1 if (req.getRequestURI().indexOf("login.jsp")!=-1||req.getRequestURI().indexOf("LoginServlet")!=-1) {
    2     filterChain.doFilter(request, response); // 如果用户请求的是login.jsp直接放行
    3     return; // 这一行代码一定要加上啊!
    4 }

      运行结果:

      上述程序看似已经没有问题,但是还存在一个小bug,当我们在登陆页输入一个错误的用户名和密码页面并不会重定向到failure.jsp而是重定向到了login.jsp。如下图所示:

      这是因为虽然我们在Servlet验证失败的时候将页面重定向到了failure.jsp,failure.jsp又被过滤器重定向到了login.jsp【因为过滤规则为所有的页面,并且session中没有用户名】

      这时我们就会发现我们在doFilter中需要例外的页面越来越多(登陆页面、成功页面、失败页面、错误页面……)。为了减轻工作的复杂度,我们可以使用过滤器的init方法中的FilterConfig对象。

     1. 在web.xml中配置不过滤页面的初始化参数:

      2.  在过滤方法中使用以上初始化参数。

     1 public class LoginFilter implements Filter{
     2 
     3     private FilterConfig filterConfig;// 声明一个FilterConfig对象
     4 
     5     public void init(FilterConfig filterConfig) throws ServletException {
     6         this.filterConfig = filterConfig; // 初始化方法的时候给filterConfig赋值
     7     }
     8     
     9     public void destroy() {
    10         
    11     }
    12 
    13     public void doFilter(ServletRequest request, ServletResponse response,
    14             FilterChain filterChain) throws IOException, ServletException {
    15         // 代码省略
    16     }
    17 
    18 }

      3. 在doFilter方法中使用FilterConfig对象获得初始化参数。

     1 public void doFilter(ServletRequest request, ServletResponse response,
     2         FilterChain filterChain) throws IOException, ServletException {
     3     HttpServletRequest req = (HttpServletRequest) request;
     4     HttpServletResponse res = (HttpServletResponse) response;
     5     HttpSession session = req.getSession();
     6     
     7     String noLoginPaths = filterConfig.getInitParameter("noLoginPaths");
     8     if (noLoginPaths!=null&&noLoginPaths.length()>0) {
     9         String[]temp = noLoginPaths.split(";");
    10         for (String string : temp) {
    11             if (string==null||"".equals(string)) {
    12                 continue;
    13             }
    14             // 例外的URL,如果符合这些URL就直接放行
    15             if (req.getRequestURI().indexOf(string)!=-1) {
    16                 filterChain.doFilter(request, response); // 如果用户请求的是login.jsp直接放行
    17                 return; // 这一行代码一定要加上啊!
    18             }
    19         }
    20     }
    21     
    22     if (session.getAttribute("username")!=null) {
    23         filterChain.doFilter(request, response); // 如果用户已经登录则放行
    24     }else {
    25         res.sendRedirect("login.jsp");
    26     }
    27 }

      这样就完成了一个比较完整的登录验证。

    编码转换

      以上面的登录页面为例,在处理登录页的LoginServlet中我们将用户提交的用户名打印出来:

    1 public void doPost(HttpServletRequest request, HttpServletResponse response)
    2             throws ServletException, IOException {
    3     String username = request.getParameter("username");
    4     System.out.println("用户提交的用户名是:" + username);
    5 
    6 }

      

     后台的输出出现了乱码!

      已知的2种处理乱码的方式:

    1 String username = request.getParameter("username");
    2 
    3 request.setCharacterEncoding("utf-8");                // 【方式1】修改请求的编码方式
    4 new String(username.getBytes("iso-8859-1"),"utf-8")   // 【方式2】使用特定编码重新构建字符串

      除此之外,还可以使用过滤器解决以上问题。

      在过滤器的doFilter方法的第一行代码加上以下代码:

    request.setCharacterEncoding("utf-8");

      这样一次设置就可以让该网站的所有页面都不会乱码(因为请求总是先到达过滤器)。但是为了更好地完成服务器的配置,我们一般在过滤器的初始化参数里配置字符集:

      然后在doFilter方法中这样调用:

     1 public class LoginFilter implements Filter{
     2 
     3     private FilterConfig filterConfig;// 声明一个FilterConfig对象
     4     
     5     public void destroy() {
     6         
     7     }
     8 
     9     public void doFilter(ServletRequest request, ServletResponse response,
    10             FilterChain filterChain) throws IOException, ServletException {
    11 
    12         String charset = filterConfig.getInitParameter("charset");
    13         if (charset == null) {
    14             charset = "utf-8"; // 如果没有指定字符集,默认是utf-8
    15         }
    16         request.setCharacterEncoding(charset); // 从初始化参数里面获得字符集
    17 
    18         // 代码省略
    19         
    20     }
    21 
    22     public void init(FilterConfig filterConfig) throws ServletException {
    23         this.filterConfig = filterConfig; // 初始化方法的时候给filterConfig赋值
    24     }
    25 
    26 }
  • 相关阅读:
    Get code into Bitbucket fast using Atlassian's SourceTree or the command line
    Django+angularJs
    修改默认python版本
    重拾python mac1.9.2
    REST
    Parameters.Add Parameters.Addrange
    sql建表前删除存在的同名表
    C#1.0
    [转]C#究竟能给开发者带来什么
    Laravel中上传图片至七牛云
  • 原文地址:https://www.cnblogs.com/happyfans/p/4370332.html
Copyright © 2020-2023  润新知