• Filter过滤器


    Filter

    一、概念

    Javaweb三大组件(Servlet、Filter、Listener)之一,Filter就是过滤器,当访问服务器资源时,Filter可以将请求拦截下来,完成一些特定的功能,也就是过滤特定的请求资源、请求信息、响应信息;当一个请求到来,Web服务器首先判断是否有过滤器与请求资源相关联,如果有那么将请求交给过滤器处理,再由过滤器决定是否交给请求资源,响应则相反。过滤器一般用于完成通用的操作,比如:登录验证、统一编码处理、敏感字符过滤……

    过滤器的基本原理:

    1. Filter就是一个实现了接口Filter的java类,与Servlet类似的由Tomcat执行
    2. 当为一个Filter配置了拦截资源,当请求该资源时,可以由该Filter决定是否放行该请求到请求的资源,以及决定是否对请求消息、响应消息作出修改
    3. 当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
    4. 但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 对象时通过 Filter.doFilter 方法的参数传递进来的。
    5. 只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行响应前后实现某些特殊功能。
    6. 如果在 Filter.doFilter 方法中没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法不会被执行,这样通过 Filter 就可以阻止某些非法的访问请求。

    二、编写Filter的步骤

    1. 定义一个类,实现接口 Filter

    2. 复写接口的方法

    3. 配置拦截路径(访问什么资源,过滤器会生效)

      @WebFilter("/*")// /*表示访问所有资源之前都会执行该过滤器,如果是/demo.jsp就是表示访问demo.jsp之前执行该过滤器
      public class FilterDemo1 implements Filter {
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      	// doFilter业务处理的核心代码区,相当于Servlet的service方法
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("FilterDemo1----------------------");
      
              filterChain.doFilter(servletRequest, servletResponse);// 过滤器放行请求,可以访问到index.jsp
          }
      
          @Override
          public void destroy() {
      
          }
      }
      

    三、过滤器的一些细节

    1. web.xml配置

    <!--先为FilterDemo1这个类配置一个过滤器的名字demo1,然后配置该过滤器应用的资源范围-->
        <filter>
            <filter-name>demo1</filter-name>
            <filter-class>cn.zhuobo.web.filter.FilterDemo1</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>demo1</filter-name>
            <url-pattern>/*</url-pattern> <!-- 这里配置的是拦截路径 -->
        </filter-mapping>
    

    2. 过滤器的执行流程

    1. 执行过滤器

    2. 执行过滤器放行后的请求资源

    3. 执行过滤器放行后的代码

      // 对request对象做一些增强
      System.out.println("filter11111111111111111");
      
      // 放行
      chain.doFilter(req, resp);
      
      //回程,对response对象做一些增强
      System.out.println("filter222222222222222222");
      

    3. 过滤器的生命周期方法

    1. init:服务器启动后创建Filter对象,调用init方法,init方法只执行一次,一般用来加载资源
    2. doFilter:每次请求被拦截的资源时都会执行,可以执行多次
    3. destroy:服务器关闭后Filter对象被销毁,如果服务器是正常关闭,就会执行destroy方法

    4. 过滤器配置细节

    1. 拦截路径的配置

      1. 拦截具体资源:index.jsp,表示只有访问该资源时对应的过滤器才会被执行
      2. 拦截目录:/dir/*,表示访问dir目录下的所有资源过滤器都会被执行
      3. 拦截后缀名:*.jsp,表示访问jsp资源时过滤器会被执行
      4. 拦截全部资源:/*,表示访问所有资源都会执行过滤器
    2. 拦截方式的配置(资源访问的方式,比如浏览器直接请求,转发请求),使用dispatcherTypes属性配置,下面为该属性的5个值:

      1. DispatcherType.REQUEST:默认值,只有直接请求该资源才会执行过滤器
      2. DispatcherType.FORWARD:转发访问该资源才执行过滤器
      3. DispatcherType.INCLUDE:包含访问资源
      4. DispatcherType.ERROR:错误跳转资源
      5. DispatcherType.ASYNC:异步访问该资源

      注意dispatcherTypes的值可以是一个数组,也就是可以配置多个值,这种情况下,无论是直接访问,还是请求转发都会执行过滤器,配置了两个值

      @WebFilter(value = "/index.jsp", dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST}) // 直接访问或者转发
      

    5. 过滤器链(配置多个过滤器)

    可以配置多个过滤器,而且可以都生效,但是过滤器有执行顺序的问题,上一个的doFilter方法激活下一个的doFilter方法,最后一个doFilter方法激活访问Servlet的service方法,过滤器链的中间任意一个filter没有调用doFilter方法,那么最终的Servlet的service方法都不会被执行:如果有两个过滤器分别是Filter1和Filter2,那么执行顺序如下:

    1. Filter1执行
    2. Filter2执行
    3. 被请求的资源执行
    4. Filter2执行
    5. Filter1执行

    还有就是过滤器的先后问题:

    1. 注解配置方式:按照类名的字符串比较顺序,值小的先执行
    2. web.xml配置方式:< filter-mapping >标签先定义者先执行

    四、过滤器的应用(登录验证、敏感词过滤)

    1. 登录验证

    1. 登录验证需求:

      1. 访问资源,验证是否已经登录
      2. 如果登录了就直接放行
      3. 否则就直接跳转发到登录页面,提示“请您先登录”
    2. 登录验证的过滤器的核心逻辑:

      1. 判断请求的资源是否登录相关的资源(登录页面相关的资源),如果是,就直接放行;否则就要判断是否已经登录

      2. 判断是否已经登录,因为如果登录了都会在session中存储user键,判断session中是否有user即可判断是否已经登录;

        1. 如果已经登录,直接放行
        2. 否则跳转到登录页面,提示“请您先登录”
      3. 具体代码实现:

            public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        
                // 将req强制转换为HttpServletRequest,因为一般都是HTTP协议的
                HttpServletRequest request = (HttpServletRequest) req;
                //1. 获取访问资源的URI,就是那个很长的
                String uri = request.getRequestURI();
                // 2. 判断uri是否包含有登录相关的资源,包括login.jsp,相关的验证码、css样式、js、字体样式等
                if(uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("checkCodeServlet") || uri.contains("/css/")|| uri.contains("/js/") || uri.contains("/fonts/")) {
                    // 含有登录相关的资源,表示用户就是要去登录的,直接放行
                    chain.doFilter(req, resp);
                } else {
                    // 否则就判断使用是否已经登录了
                    Object user = request.getSession().getAttribute("user");
                    if (user != null) {
                        // 已经登录的,就直接放行
                        chain.doFilter(req, resp);
                    } else {
                        // 否则就跳转到登录页面,并且提示要先登录
                        request.setAttribute("login_msg", "您还没有登录,请您先登录!");
                        request.getRequestDispatcher("/login.jsp").forward(request, resp);
                    }
                }
        
            }
        
        

    2. 敏感词汇过滤

    1. 敏感词汇过滤需求:

      1. 对录入数据进行敏感词汇过滤
      2. 敏感词汇参考《敏感词汇.txt》
      3. 将敏感词替换为***
    2. 分析:

      1. 请求参数是封装在request对象的
      2. Filter过滤器doFilter的参数req对象事实上和封装请求参数的request对象时同一个
      3. 在doFilter中将请求参数中的敏感词汇替换为***,重新封装request对象
      4. 放行,chain.doFilter的参数传入重新封装的request对象
    3. 增强对象的功能,采用设计模式中的装饰模式、代理模式可增强对象的功能

      1. 装饰模式

      2. 代理模式:代理对象代理真实对象,达到增强真实对象功能的模式

        • 真实对象:被代理的对象
        • 代理对象:

        实现方式:

        • 静态代理:有一个类文件描述代理模式
        • 动态代理:在内存中形成代理
      3. 动态代理的实现步骤

        1. 代理对象和真实对象实现相同的接口(代理对象和真实对象时兄弟);
        2. 代理对象 = Proxy.newProxyInstance();
        3. 使用代理对象调动方法;
        4. 增强方法。
      4. 增强方法的方式

        1. 增强参数列表
        2. 增强返回值类型
        3. 增强方法体执行逻辑
        public class ProxyTest {
            public static void main(String[] args) {
                Huawei huawei = new Huawei();
                /**
                 *Proxy.newProxyInstance方法的三个参数:
                 * 1. 真实对象的类加载器:真实对象.getClass().getClassLoader()
                 * 2. 接口数组:代理对象实现的接口,真实对象.getClass().getClassLoader()
                 * 3. 处理器:new InvocationHandler()
                 */
                SaleComputer proxy_huawei = (SaleComputer) Proxy.newProxyInstance(huawei.getClass().getClassLoader(), huawei.getClass().getInterfaces(), new InvocationHandler() {
                    /**
                     *invoke方法是代理逻辑编写的方法,代理对象调用的所有方法都会触发该方法的执行
                     *
                     * @param proxy:代理对象
                     * @param method:代理对象调用的方法,被封装成的对象
                     * @param args:代理对象调用方法时,传递的参数封装的数组
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 判断执行的方法是不是sale方法,如果是就增强参数(修改参数)
                        if("sale".equals(method.getName())) {
                            double money = (double) args[0];
                            money = money * 0.8;// 改变参数的值
                            // 使用真实对象调用方法
                            String obj = (String) method.invoke(huawei, money);
        
                            // 增强返回值
                            return obj + "、电脑包、鼠标垫......";
                        } else {// 如果不是sale方法,那么就原样执行
                            // 使用真实对象调用sale方法
                            Object obj = method.invoke(huawei, args);
                            return obj;
                        }
                    }
                });
        
                // 代理对象调用sale方法
                String computer = proxy_huawei.sale(7788);
                System.out.println(computer);
                
                proxy_huawei.show();
            }
        }
        
  • 相关阅读:
    Photoshop 2021 for Mac
    viscose live serves 扩展工具更改默认自动打开的浏览器
    UML面向对象分析、建模与设计
    Shell 脚本
    早做打算,不要随遇而安。
    编程人员成长模型
    Spring AOP详解
    Mybatis逆向工程的配置
    Int和String互转的方法
    SQL学习
  • 原文地址:https://www.cnblogs.com/zhuobo/p/10825750.html
Copyright © 2020-2023  润新知