在自定义 Servlet Filter 中抛出错误时多次调用 doFilter 方法
doFilter method called multiple times when an error is throw in a custom Servlet Filter
我已经实施了一个过滤器来检查具有 Spring 安全性的 IP 地址白名单。
它有效,但如果我在 doFilter 方法中抛出错误,则抛出被调用 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 安全性的相同方法,但我仍然有 3 次抛出。
所以我开始明白为什么,看看日志:
第一次投掷
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]
感谢您的帮助!
您 运行 在 Servlet 环境中。 servlet 规范并没有说您应该通过抛出异常来响应错误。相反,您应该使用 HTTP 状态代码和消息进行响应。当您抛出异常时,servlet 容器将尽最大努力确定要做什么,但通常这会导致向用户返回 500 内部服务器错误,这既不准确也没有帮助。
仅从过滤器返回也不正确,因为您没有将任何内容放入 HTTP 响应中。同样,您应该在响应中设置适当的状态代码,例如 403 Forbidden,它会向调用者指示某些配置不正确。
为了回答我的问题,我的过滤器终于可以正常工作了,正如@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 {
}
}
我已经实施了一个过滤器来检查具有 Spring 安全性的 IP 地址白名单。 它有效,但如果我在 doFilter 方法中抛出错误,则抛出被调用 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 安全性的相同方法,但我仍然有 3 次抛出。 所以我开始明白为什么,看看日志:
第一次投掷
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]
感谢您的帮助!
您 运行 在 Servlet 环境中。 servlet 规范并没有说您应该通过抛出异常来响应错误。相反,您应该使用 HTTP 状态代码和消息进行响应。当您抛出异常时,servlet 容器将尽最大努力确定要做什么,但通常这会导致向用户返回 500 内部服务器错误,这既不准确也没有帮助。
仅从过滤器返回也不正确,因为您没有将任何内容放入 HTTP 响应中。同样,您应该在响应中设置适当的状态代码,例如 403 Forbidden,它会向调用者指示某些配置不正确。
为了回答我的问题,我的过滤器终于可以正常工作了,正如@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 {
}
}