Alice52 / spring-5.2.x

spring source code
https://github.com/Alice52/spring-5.2.x/issues/2
0 stars 1 forks source link

[spring] Filter #31

Closed Alice52 closed 3 years ago

Alice52 commented 3 years ago

OncePerRequestFilter

  1. OncePerRequestFilter 是在一次外部请求中只过滤一次, 对于服务器内部之间的forward等请求, 不会再次执行过滤方法
  2. Spring Security里面被广泛用到
  3. 通过增加标记的方式来实现过滤器只被执行一次

    public abstract class OncePerRequestFilter extends GenericFilterBean {
            //一个标记,后面会用到
        public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
    
        @Override
        public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
    
            if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
                throw new ServletException("OncePerRequestFilter just supports HTTP requests");
            }
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            //这里获取一个名称,该名称后面会被用于放到request当作key
            String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
            //检测当前请求是否已经拥有了该标记,如果拥有该标记则代表该过滤器执行过了(后面注释有说明)
            boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
    
            if (skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {
    
                // Proceed without invoking this filter...
                filterChain.doFilter(request, response);
            }
            //如果此过滤器已经被执行过则执行如下的逻辑
            else if (hasAlreadyFilteredAttribute) {
    
                if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
                    doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
                    return;
                }
    
                // Proceed without invoking this filter...
                filterChain.doFilter(request, response);
            }
            //走到这里说明该过滤器没有被执行过
            else {
                // Do invoke this filter...
                // 在当前请求里面设置一个标记,key就是前面拼接的那个变量,value是true,这个标记如果在request存在则在前面会被检测到并改变hasAlreadyFilteredAttribute的值
                request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
                try {
                                    // 这个方法是一个抽象方法需要子类去实现具体的过滤逻辑
                    doFilterInternal(httpRequest, httpResponse, filterChain);
                }
                finally {
                    // Remove the "already filtered" request attribute for this request.
                    // 执行完毕之后移除该标记
                    request.removeAttribute(alreadyFilteredAttributeName);
                }
            }
        }
        //其余代码略
    }