• 过滤器中的chain.doFilter(request,response)


    Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一设置编码,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。

    简单来说,过滤器filter的作用,它就相当于公路上的一个关卡,对要通过的拦截下来进行相关操作或放行。

    dofilter作用【request -> filter1 -> filter2 ->filter3 -> .... -> request resource。】

    看一个简单的没有filter的登录例子,当用户名和密码都是admin时,能跳转到success页面,否则到fail页面。

    1、eclipse建立一个web project ,结果目录如下

    其中,jsp很简单。在login.jsp为一个form表单

    1   <form action="<%=request.getContextPath() %>/servlet/LoginServlet" method="post">
    2   用户名:<input type="text" name="username">
    3   密码:<input type="password" name="password">
    4  <input type="submit" value="提交"> 
    5 </form>

    配置文件xml中为

     1   <servlet>
     2     <description>This is the description of my J2EE component</description>
     3     <display-name>This is the display name of my J2EE component</display-name>
     4     <servlet-name>LoginServlet</servlet-name>
     5     <servlet-class>com.imooc.serlvet.LoginServlet</servlet-class>
     6   </servlet>
     7 
     8   <servlet-mapping>
     9     <servlet-name>LoginServlet</servlet-name>
    10     <url-pattern>/servlet/LoginServlet</url-pattern>
    11   </servlet-mapping>    

    LoginServlet.java中主要是

     1     public void destroy() {
     2         super.destroy(); // Just puts "destroy" string in log
     3         // Put your code here
     4     }
     5 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     6         String username = request.getParameter("username");
     7         String password = request.getParameter("password");
     8         
     9         System.out.println(username);
    10         
    11         if("admin".equals(username) && "admin".equals(password)){
    12             //校验通过
    13             HttpSession session = request.getSession();
    14             session.setAttribute("username", username);
    15             response.sendRedirect(request.getContextPath()+"/sucess.jsp");
    16         }else{
    17             //校验失败
    18             response.sendRedirect(request.getContextPath()+"/fail.jsp");
    19         }
    20         
    21     }
    22 public void init() throws ServletException {
    23         // Put your code here
    24     }

    运行后浏览器地址栏进入:...8080/LoginFilter/login.jsp

    当用户名和密码都输入admin,按下submit,就会进入action对应的servlet,然后重定向到success.jsp页面;用户名密码不对就到fail.jsp。

    小例子结束。

    仔细想会想到,可以直接在地址输入8080/LoginFilter/success.jsp不就进入了成功的页面了吗,还有类似页面。这样时不安全的,所以要加入拦截器

    2、在此项目加入拦截器filter,也就是对其url进行拦截,不让其用url随便访问,结构中多了个类文件

    在xml中添加

     1  <filter>
     2        <filter-name>LoginFilter</filter-name>
     3        <filter-class>com.imooc.filter.LoginFilter</filter-class>
     4  </filter>
     5  <filter-mapping>
     6        <filter-name>LoginFilter</filter-name>
     7        <url-pattern>/success.jsp</url-pattern>
     8  </filter-mapping>

     这样,url输入...8080/LoginFilter/success.jsp就会进行拦截,进入LoginFilter.java

     里面主要为:

     1   @Override
     2       public void destroy() {
     3   System.out.println("销毁...");  
     4       }
     5       @Override
     6       public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
     7           HttpServletRequest request = (HttpServletRequest) arg0;
     8           HttpServletResponse response = (HttpServletResponse) arg1;
     9           HttpSession session = request.getSession();
    10   //因为登录后保存了username,所以可以先检查username判断是否登录
    11  if(session.getAttribute("username")!=null){
    12              arg2.doFilter(arg0, arg1);//已登录,则放行,
    13          }else{
    14              response.sendRedirect("login.jsp");//未登录,重定向到登录页面
    15          }
    16      }
    17 public void init(FilterConfig arg0) throws ServletException  
    18   {  
    19 
    20      System.out.println("初始化...");  
    21 
    22     }  

     【过滤器Filter生命周期:init()->doFilter()->destroy()】

     【 出现了 arg.doFailter(arg0,arg1),他表示好像放行、通过这样,继续往下到LoginServlet.java中,如果没有写 arg.doFailter(arg0,arg1)。那就是,把他拦截下来后,什么都不做,也不让它通过,那就会卡在那里不能往下了。】

     【在这里只是request -> filter1 -> request resource。】

    这样的思路,就可以拦截一些不能随便访问的页面,但如果这类页面很多,可访问的页面相对少,则可以把拦截的地址改为/*,也就是

    1    <filter-mapping>
    2          <filter-name>LoginFilter</filter-name>
    3          <url-pattern>/success.jsp</url-pattern>
    4    </filter-mapping>
    5 改成  
    6  <filter-mapping>
    7          <filter-name>LoginFilter</filter-name>
    8          <url-pattern>/*</url-pattern>
    9    </filter-mapping>

     再对本不用拦截的,比如login.jsp 在LoginFilter进行判断

     1      public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
     2          HttpServletRequest request = (HttpServletRequest) arg0;
     3          HttpServletResponse response = (HttpServletResponse) arg1;
     4         HttpSession session = request.getSession();
     5 //如果是login.jsp,则不拦截,直接放行,不用进行其他操作
     6 if(request.getRequestURI().indexOf("login.jsp")!=-1 ){
     7                     arg2.doFilter(arg0, arg1);
     8                     return;
     9                 }
    10 
    11 //因为登录后保存了username,所以可以先检查username判断是否登录
    12  if(session.getAttribute("username")!=null){
    13              arg2.doFilter(arg0, arg1);//已登录,则放行,
    14          }else{
    15              response.sendRedirect("login.jsp");//未登录,重定向到登录页面
    16          }

     仔细想想,其他问题也来了,因为拦截所有页面,所以按下submit时都过不去(action="<%=request.getContextPath() %>/servlet/LoginServlet" 也要拦截,过不去),则要添加一些不需拦截的url

    1 //如果是下面3个url,则不拦截,直接放行,不用进行其他操作
    2  if(request.getRequestURI().indexOf("login.jsp")!=-1 
    3   ||request.getRequestURI().indexOf("servlet/LoginServlet")!=-1 
    4   ||request.getRequestURI().indexOf("fail.jsp")!=-1 
    5 ){
    6                    arg2.doFilter(arg0, arg1);
    7                 return;
    8                 }

    但这样也有问题,当不需拦截的url多了,if语句也屡屡需要修改,很麻烦,则可以用 FilterConfig对象。先在xml添加配置,有新的不需要拦截的url,秩序在配置里添加即可。

     1     <filter>
     2         <filter-name>LoginFilter</filter-name>
     3         <filter-class>com.imooc.filter.LoginFilter</filter-class>
     4         <init-param>
     5             <param-name>noLoginPaths</param-name>
     6             <param-value>login.jsp;fail.jsp;LoginServlet</param-value> //在此添加不需拦截的url
     7         </init-param>
     8         <init-param>
     9             <param-name>charset</param-name> //防止中文乱码
    10             <param-value>UTF-8</param-value>
    11         </init-param>
    12     </filter>
    13     <filter-mapping>
    14         <filter-name>LoginFilter</filter-name>
    15         <url-pattern>/*</url-pattern>
    16     </filter-mapping>

    在dofilter中得到此对象 LoginFilter.java 

     1 public class LoginFilter implements Filter {
     2     private FilterConfig config;  //这里定义一下
     3     @Override
     4     public void destroy() {
     5 
     6     }
     7     @Override
     8     public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
     9 
    10         HttpServletRequest request = (HttpServletRequest) arg0;
    11         HttpServletResponse response = (HttpServletResponse) arg1;
    12         HttpSession session = request.getSession();
    13         String noLoginPaths = config.getInitParameter("noLoginPaths"); //已获取filterconfig对象,可以获取其中属性
    14         
    15         String charset = config.getInitParameter("charset");
    16         if(charset==null){
    17             charset = "UTF-8";
    18         }
    19         request.setCharacterEncoding(charset);
    20         
    21         if(noLoginPaths!=null){
    22             String[] strArray = noLoginPaths.split(";");  //对属性的值分割。分别放行
    23             for (int i = 0; i < strArray.length; i++) {
    24                 if(strArray[i]==null || "".equals(strArray[i]))continue;
    25         if(request.getRequestURI().indexOf(strArray[i])!=-1 ){
    26                     arg2.doFilter(arg0, arg1);
    27                     return;
    28                 }
    29             }
    30         }
    31         if(session.getAttribute("username")!=null){
    32             arg2.doFilter(arg0, arg1);
    33         }else{
    34             response.sendRedirect("login.jsp");
    35         }
    36     }
    37     @Override
    38     public void init(FilterConfig arg0) throws ServletException {
    39         config = arg0;  //在初始化时把次对象赋值
    40     }
    41 
    42 }
    //执行顺序:最先初始化init(),然后dofilter函数 最后destory()

     这里,这个登录案例就完成了。

    那【request -> filter1 -> filter2 ->filter3 -> .... -> request resource。】是什么呢

    就是一个url-partten对应了多个filter,一个url,被拦截了好几次。

     1   <filter>
     2         <filter-name>FirstFilter</filter-name>
     3         <filter-class>com.imooc.filter.FirstFilter</filter-class>
     4  </filter>
     5     <filter>
     6         <filter-name>SecondFilter</filter-name>
     7         <filter-class>com.imooc.filter.SecondFilter</filter-class>
     8     </filter>
     9 
    10     <filter-mapping>
    11         <filter-name>FirstFilter</filter-name>
    12         <url-pattern>/index.jsp</url-pattern> 
    13     </filter-mapping>
    14    <filter-mapping>
    15         <filter-name>SecondFilter</filter-name>
    16         <url-pattern>/index.jsp</url-pattern>
    17     </filter-mapping>

     这样就存在一个过滤器链,按xml中顺序执行。

    index.jsp

    1   <body>
    2     This is my JSP page. <br>
    3     <%
    4     System.out.println("到了index.jsp");
    5      %>
    6   </body>

    FirstFilter.java

     1 public class FirstFilter implements Filter {
     2 
     3     @Override
     4     public void destroy() {
     5         System.out.println("destroy---FirstFilter");
     6     }
     7 
     8     @Override
     9     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    10         System.out.println("start----doFilter--FirstFilter");
    11         chain.doFilter(request, response);
    12         HttpServletRequest req =(HttpServletRequest) request;
    13         HttpServletResponse response2 =(HttpServletResponse) response;
    
    19         System.out.println("end------doFilter--FirstFilter");
    20     }
    21 
    22     @Override
    23     public void init(FilterConfig filterConfig) throws ServletException {
    24         System.out.println("init----FirstFilter");
    25     }
    26 
    27 
    28 }

    SecondFilter.java

     1 public class SecondFilter implements Filter {
     2 
     3     @Override
     4     public void destroy() {
     5         System.out.println("destroy-----SecondFilter");
     6     }
     7 
     8     @Override
     9     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    10         System.out.println("start---doFilter--SecondFilter");
    11         chain.doFilter(request, response);
    12         System.out.println("end---doFilter--SecondFilter");
    13     }
    14 
    15     @Override
    16     public void init(FilterConfig filterConfig) throws ServletException {
    17 
    18         System.out.println("init-----SecondFilter");
    19     }
    20 
    21 }

    过滤器链具体的执行顺序:

    在运行项目时,两个filter类都执行了init()初始化,控制台输出:

    init-----FirstFilter
    init-----SecondFilter

    当访问index.jsp时,进入FirstFilter.java的dofilter函数,控制台会依次输出:

    start---doFilter--FirstFilter
    start---doFilter--SecondFilter
    到了index.jsp
    end---doFilter--FirstFilter
    end---doFilter--SecondFilter

     在dofilter函数中,先执行chain.doFilter(request, response);前的代码,然后下一个filter链的下一个filter,然后进入index.jsp。再继续依次执行chain.doFilter(request, response);后面的代码

     Code1表示ilter(request, response);之前的代码Code2表示ilter(request, response);之后的代码。

     文章为学习记录,若有错误,望指正。

  • 相关阅读:
    Tiny_4412的NFS挂载
    tiny4412学习一:编译uboot,体验裸机
    开通博客,记录历程,开启新的征程
    mysql 多表联合做运算(求俩点的距离)
    golang gin框架使用图形验证码
    js rgb和16进制相互转换
    [转载] Centos7的安装、Docker1.12.3的安装,以及Docker Swarm集群的简单实例
    openstack golang sdk使用
    sendcloud golang 发送短信 示例代码
    Harbor配置https,并安装内容信任插件(notary)
  • 原文地址:https://www.cnblogs.com/ooooevan/p/5727798.html
Copyright © 2020-2023  润新知