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 }

 

posted @ 2015-03-26 22:44  夜已殇  阅读(1132)  评论(0编辑  收藏  举报