【问题标题】:doFilter method called multiple times when an error is throw in a custom Servlet Filter在自定义 Servlet 过滤器中抛出错误时多次调用 doFilter 方法
【发布时间】:2020-05-30 16:55:47
【问题描述】:

我已经使用 Spring Security 实现了一个过滤器来检查 IP 地址白名单。 它可以工作,但是如果我在 doFilter 方法中抛出错误,则该 throw 被调用 3 次 oO。

我找到了一个带有“return;”的解决方案,但我对此并不满意。 这意味着我必须在不使用 throw 的情况下记录我的错误...

你觉得怎么样,有没有更好的办法?最佳实践?

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这是网页配置

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
                http.authorizeRequests()
                        .antMatchers("/**").permitAll().and()
                        .addFilterBefore(new CustomIpFilter(),
                                BasicAuthenticationFilter.class)
                        .csrf().disable()
                        .formLogin().disable();
        }
    }

这是我的过滤器

    @Log4j2
    @WebFilter
    public class CustomIpFilter implements Filter {

        Set<String> whitelist = new HashSet<>();

        public CustomIpFilter() {
            whitelist.add("0:0:0:0:0:0:0:1"); //localhost
            whitelist.add("127.0.0.1");
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {

            String ipAdress = request.getRemoteAddr();
            if (!whitelist.contains(ipAdress)) {
                log.error("Unknown IP adress");
                /* if the return is replaced by throw line, it still works, but doFilter will be called 3 times and throw 3 times the same error
                throw new UnknownHostException("Unknown IP adress");*/
                return;
            }
            chain.doFilter(request, response); //Continue
        }

        @Override
        public void destroy() {

        }

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {

        }
    }

用于测试的控制器

@RestController
@RequestMapping(value = "test")
public class LoggerController {

    @GetMapping("/go")
    public String logsTest() {
        System.out.println("ok");
        return "Ok";
    }
}

我已经尝试过使用“preHandle”方法删除 Spring Security 的拦截器,但我仍然有 3 次 Throw。 所以我开始明白为什么了,看看日志:

第一次投掷

java.net.UnknownHostException: Unknown IP adress

第二次投掷

[nio-8181-exec-5] c.e.d.S.IpAdressInterceptor              : Unknown IP adress
[nio-8181-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] threw exception

第三次投掷:

[nio-8181-exec-5] o.a.c.c.C.[Tomcat].[localhost]           : Exception Processing ErrorPage[errorCode=0, location=/error]

感谢您的帮助!

【问题讨论】:

    标签: java spring security servlets filter


    【解决方案1】:

    您在 Servlet 环境中运行。 servlet 规范并没有说您应该通过抛出异常来响应错误。相反,您应该使用 HTTP 状态代码和消息进行响应。当您抛出异常时,servlet 容器将尽最大努力弄清楚要做什么,但经常会导致向用户返回 500 Internal Server Error,这是不准确或无用的。

    仅从过滤器返回也不正确,因为您没有在 HTTP 响应中添加任何内容。同样,您应该在响应中设置适当的状态代码,例如 403 Forbidden,它会向调用者指示某些配置不正确。

    【讨论】:

    • 是的,这就是我最终决定解决这个问题的方法:java HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.getWriter().write(message); httpResponse.getWriter().flush(); httpResponse.getWriter().close(); return;
    • 如果您认为我的回答正确,我将不胜感激。
    【解决方案2】:

    为了回答我的问题,我的过滤器终于可以正常工作了,正如@rgoers 所解释的那样

    @Log4j2
    @WebFilter
    public class CustomIpFilter implements Filter {
    
        private final Set<String> whitelist = new HashSet<>();
        private final String unauthorizedIpAddressMessage = "CONNECTION REFUSED: Unauthorized IP address: ";
    
        public CustomIpFilter() {
            whitelist.add("0:0:0:0:0:0:0:1"); //localhost
            whitelist.add("127.0.0.1");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
    
            String ipAddress = request.getRemoteAddr();
    
            if (!isAuthorizedIpAdress(ipAddress)) {
                log.error(unauthorizedIpAddressMessage + ipAddress);
                setUnauthorizedHttpServletResponse(response, unauthorizedIpAddressMessage + ipAddress);
                return;
            }
    
            chain.doFilter(request, response); //Continue
        }
    
        public void setUnauthorizedHttpServletResponse(ServletResponse response, String message)
                throws IOException {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.getWriter().write(message);
            httpResponse.getWriter().flush();
            httpResponse.getWriter().close();
        }
    
        public boolean isAuthorizedIpAdress(String ipAddress) {
            if (Utils.isElementInSetList(whitelist, ipAddress)) {
                return true;
            }
            return false;
        }
    
        @Override
        public void destroy() {
    
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-21
      • 1970-01-01
      • 2017-05-14
      • 2010-10-15
      • 1970-01-01
      相关资源
      最近更新 更多