java web 之我对filter的理解

概述

在web开发中,想要统一拦截某些请求,实现一些功能。还要能解耦,不与具体业务耦合,这个时候,就可以考虑用filter来做一些事情了。
而filter是怎么实现的呢? 这个肯定与servlet的流转过程有关系了。
从使用的角度来看,
1、一个请求发到web容器,容器通过servlet进行分发,
2、然后容器(①这里有疑问,到底是谁在操控流程的执行?
检查应用中注册的filter,
根据过滤器的先后顺序,
判断分发方式是否符合,
判断本次请求的url是否匹配filter的映射规则,
然后进入到各filter。
3、到了filter内部,即可拿到本次请求的上下文(②需要了解请求上文下是如何包装起来的?),可以做一些切面性质的工作了。
4、如何跳转下一个filetr,则是调用入参中的filetChain的dofilter 进入到下一个filter或是对应的service服务中去。

至于filter的创建、初始化、销毁,则是一次性的。在spring中是由spring进行管理的,创建时进行init操作。而销毁则是伴随着应用程序的销毁而销毁。(③这点需考证?

用法

1、注册过滤器
使用spring进行注册的写法

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

spring boot中的写法

    @Bean
    public FilterRegistrationBean registerCheckFilter() {
        FilterRegistrationBean registerBean = new FilterRegistrationBean();
        //设置过滤哪些路由
        this.setDispatcherTypes(registerBean);
        //此处省略了设置initParam的代码
        //不设置匹配哪些url,则默认匹配/*
        registerBean.setOrder(0);
        registerBean.setFilter(new CheckEnterCasFilter());
        return registerBean;
    }

    private void setDispatcherTypes(FilterRegistrationBean registerBean) {
        EnumSet<DispatcherType> dispatcherTypes = EnumSet.noneOf(DispatcherType.class);
        dispatcherTypes.add(DispatcherType.INCLUDE);
        dispatcherTypes.add(DispatcherType.REQUEST);
        dispatcherTypes.add(DispatcherType.ASYNC);
        dispatcherTypes.add(DispatcherType.ERROR);

        registerBean.setDispatcherTypes(dispatcherTypes);
    }

2、编写过滤器内部代码

public class CheckEnterCasFilter implements Filter {

    private CasProperties casProperties;

    public CheckEnterCasFilter(CasProperties casProperties) {
        this.casProperties = casProperties;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       //filterConfig可以拿到过滤器配置的initParam的参数,通过它可以给filter传进来一些可配置的选项数据
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String servletPath = httpRequest.getServletPath();
        //定制需求代码
        LogHelper.monitor(httpRequest.getRequestURI() + "进入调用链");
        //进入到过滤器调用链中,如果return则直接返回给调用方了。
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

使用场景

1、对接第三方的统一登录认证cas(center authentication server)

  • 可以设置过滤器拦截到自己系统中的访问首页的请求,
  • 判断我方的session是否有效,无效重定向到第三方cas ,
  • 第三方cas 判断session是否过滤,进行登录后再重定向到url中带的我方系统的访问链接,
  • 我方系统再次拦截回来的url请求,截获其中包含的关键认证信息,获取人员的相关信息,存到我方的session中

2、防止xss攻击
3、请求中的字符集设置

踩坑记录

1、在做使用场景1时,
需要跳过某些过滤器,就找到了一篇stackoverflow上的博客How to skip a filter in the filter chain in java
,其中提到可以使用request.getRequestDispatcher(servletPath).forward(request, response); 直接访问资源服务。由于不了解filter在过滤时,还会校验request请求的dispatcherType。
导致想跳过AFilter时,始终会重新跳到AFilte中。这样重复进入AFilter的dofilter ,马上栈帧就被撑爆了,导致栈溢出。
解决方法就是上面提到的,我们在设置过滤器时,需要设置这个过滤器能够匹配的请求的分发类型。
简单介绍下,分发类型共有5种。

public enum DispatcherType {
    FORWARD,//转发的
    INCLUDE,//包含在页面的
    REQUEST,//请求的
    ASYNC,//异步的
    ERROR;//出错的
}

http请求的分发类型(④这里的include、aync、error需深入研究下

  • REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
  • INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
  • FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
  • ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。

原理

这部分的知识,需要了解tomcat 服务器的运行方式。
目前通过网上一遍博客[3],大致了解流程是,
1.tomcat的连接器(connector)接收到请求后,创建了request、response对象,
2.然后上下文处理器 对请求对象进行wrapper包装,
3.获取到对应的servlet对象(⑤servlet的用法有遗忘?),
4.上下文处理器之后再通过request和servlet 获取过滤器调用链,
5.这时候上下文处理器就会查找当前请求的url与哪些过滤器能够匹配上的,
6.然后一个一个通过过滤器进行过滤,
7.直到过滤器走完了,最后再来调用servlet中的service()

至于service()调完了,剩下的流程应该怎么走呢? 猜想应该还是通过上下文处理器将响应返回给connector,connector再把响应转成2进制数据返回给请求方(浏览器或其他客户端)

后续 读完《深入剖析Tomcat(中文版)》再来详细阐述这个运行机制

源码解析

参考链接

[1]Java Web之过滤器(Filter)
[2]Java三大器之过滤器(Filter)的工作原理和代码演示
[3]tomcat源码分析之filter和servlet

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值