问题描述
我尝试使用spring-cloud-zuul-ratelimit限制到我的网关的传入请求,并在遵循文档后返回429个太多请求 docs是here
注意: 我使用spring 2.2.7和JDK8
问题仍然存在,并且经过长时间的投入,我认为它们是Filters中的一些问题,因为它转到LogFilterService.java中的filterchain.dofilter并在所有情况下均以正确的响应返回,然后消失并显示500内部服务器错误而没有任何清除仅例外 com.netflix.zuul.exception.ZuulException:429 因此,我将使用所有自定义过滤器来尝试发现错误
这是日志中的例外情况
2020-11-12T15:40:22.794Z (UTC+0),[http-nio-8085-exec-8] WARN o.s.c.n.z.f.post.SendErrorFilter - Error during filtering
com.netflix.zuul.exception.ZuulException: 429 TOO_MANY_REQUESTS
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitExceededException.<init>(RateLimitExceededException.java:13)
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.filters.RateLimitPreFilter.lambda$run$0(RateLimitPreFilter.java:125)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.filters.RateLimitPreFilter.run(RateLimitPreFilter.java:87)
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117)
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193)
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157)
at com.netflix.zuul.FilterProcessor.preRoute(FilterProcessor.java:133)
at com.netflix.zuul.ZuulRunner.preRoute(ZuulRunner.java:105)
at com.netflix.zuul.http.ZuulServlet.preRoute(ZuulServlet.java:125)
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:74)
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:166)
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:45)
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52)
at org.springframework.web.servlet.dispatcherServlet.dodispatch(dispatcherServlet.java:1040)
at org.springframework.web.servlet.dispatcherServlet.doService(dispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at brave.servlet.TracingFilter.doFilter(TracingFilter.java:68)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.orange.combo.gatewayservice.config.httpLoggingFilter.RequestLogFilter.doFilter(RequestLogFilter.java:98)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.logout.logoutFilter.doFilter(logoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at brave.servlet.TracingFilter.doFilter(TracingFilter.java:87)
at org.springframework.cloud.sleuth.instrument.web.LazyTracingFilter.doFilter(TraceWebServletAutoConfiguration.java:139)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcmetricsFilter.doFilterInternal(WebMvcmetricsFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.orange.combo.gatewayservice.config.apiFactoryFilter.HttpRequestCustomFilter.doFilter(HttpRequestCustomFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.socketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
@Component
public class RequestLogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger("kibana-logger");
private final String regex;
public RequestLogFilter() {
this.regex = Stream
.of(".*/v2/api-docs",".*/swagger-resources",".*/swagger-ui.html",".*/webjars",".*/bot-management/getCampaign")
.reduce((str1,str2) -> String.format("%s|%s",str1,str2))
.map(str -> String.format("(%s).*",str))
.orElse("");
}
@Override
public void init(FilterConfig filterConfig) throws servletexception {
}
@Override
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,servletexception {
final String path = ((HttpServletRequest) request).getServletPath();
if (!path.matches(this.regex)) {
request = new CustomHttpServletRequestWrapper((HttpServletRequest) request);
response = new CustomHttpServletResponseWrapper((HttpServletResponse) response);
if (((HttpServletRequest) request).getHeader("authorization") != null) {
final String rolePrefix = "ROLE_";
final String defaultRoleName = "ROLE_GUEST";
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String email = authentication.getName();
final String roleName = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).filter(authority -> authority.startsWith(rolePrefix)).findFirst().orElse(defaultRoleName)
.replaceFirst(rolePrefix,"");
MDC.put("email",email);
MDC.put("role",roleName);
} else {
MDC.remove("email");
MDC.remove("role");
}
if (((HttpServletRequest) request).getHeader("host") != null) {
final String host = ((HttpServletRequest) request).getHeader("host");
MDC.put("host",host);
}
/* request info */
final String method = ((HttpServletRequest) request).getmethod();
final String url = ((HttpServletRequest) request).getRequestURL().toString();
logger.info(String.format("REST,request,%s,%s",method,url));
/* request headers */
final List<String> requestHeaders = new LinkedList<>();
final Enumeration<String> headerNames = ((HttpServletRequest) request).getHeaderNames();
while (headerNames.hasMoreElements()) {
final String headerName = headerNames.nextElement();
if (!headerName.equals("authorization")) {
final String headerValue = ((HttpServletRequest) request).getHeader(headerName);
requestHeaders.add(String.format("%s: %s",headerName,headerValue));
}
}
logger.info(String.format("REST,headers,requestHeaders.toString()));
/* request data */
final String requestData = ((CustomHttpServletRequestWrapper) request).getBody().replaceAll("\n","");
final StringBuilder stringBuilder = new StringBuilder();
if (request.getParameterMap().size() > 0) {
stringBuilder.append("{");
request.getParameterMap().forEach((key,value) -> stringBuilder.append(key).append(": ").append(value[0]).append(","));
stringBuilder.delete(stringBuilder.length() - 2,stringBuilder.length());
stringBuilder.append("}");
}
logger.info(String.format("REST,data,requestData,stringBuilder.toString()));
/* response */
try {
// --problem is hear
chain.doFilter(request,response);
response.flushBuffer();
}finally {
final String removingWhitespacesRegex = "\n";
final Integer status = ((CustomHttpServletResponseWrapper) response).getStatus();
final String contentType = response.getContentType();
String responseData = ((CustomHttpServletResponseWrapper) response).getBody().replaceAll(removingWhitespacesRegex,"");
if (status < 400)
logger.info(String.format("REST,response,%d,status,contentType,responseData));
else
logger.error(String.format("REST,responseData));
}
} else {
chain.doFilter(request,response);
}
}
@Override
public void destroy() {
}
}
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HttpRequestCustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws servletexception {
}
@Override
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain) throws IOException,servletexception {
CustomHeadersHttpServletRequestWrapper request = new CustomHeadersHttpServletRequestWrapper((HttpServletRequest) servletRequest);
String comboToken = request.getHeader("Combo-Token");
if(Objects.nonNull(comboToken)) {
request.addHeader("Authorization",request.getHeader("Combo-Token"));
}
filterChain.doFilter(request,servletResponse);
}
@Override
public void destroy() {
}
}
@Component
public class UserFilter extends ZuulFilter {
private final UserRepository userRepository;
private final HttpErrorHandlingService errorHandlingService;
@Value("${token.validitySeconds}")
private long validitySeconds;
private static final Logger LOGGER = LoggerFactory.getLogger("kibana-logger");
public UserFilter(UserRepository deletedUserRepository,HttpErrorHandlingService errorHandlingService) {
this.userRepository = deletedUserRepository;
this.errorHandlingService = errorHandlingService;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE; //Executed before the request is routed
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true; //Indicates that run() method should be invoked
}
@Override
@SuppressWarnings("unchecked")
public Object run() {
//A wrapper around the request,shared by all filters and is unique to each request
RequestContext requestContext = RequestContext.getCurrentContext();
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String email = authentication.getName();
final User user = userRepository.findByEmail(email);
try {
//Set Response Body here
if(Objects.nonNull(user)) {
if(user.isDeleted())
buildresponse(requestContext,HttpStatus.FORBIDDEN,OrangeErrorInfo.FORBIDDEN_USER,"User is deleted");
else {
//The JWT details are only available for secured routes (i.e. routes requiring access token)
if(authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
final Map<String,Object> jwtClaims = (Map<String,Object>)((OAuth2AuthenticationDetails)authentication.getDetails())
.getDecodedDetails();
if(isInvalidToken(user.getTokenLastUpdated(),(Long)jwtClaims.get("exp")))
buildresponse(requestContext,HttpStatus.UNAUTHORIZED,OrangeErrorInfo.EXPIRED_CREDENTIALS,"COMBO token expired");
}
}
}
} catch (IOException e) {
LOGGER.error("Error while serializing object to json");
}
return null;
}
/**
* builds a response depending on the request context and client error
* @param requestContext the current request context
* @param httpStatus the response HTTP status code
* @param orangeErrorInfo the custom error code and message (ODI standardized)
* @param errorMessage the custom error description message
* @throws IOException if converting the response body object to JSON has Failed (propagated)
*/
private void buildresponse(RequestContext requestContext,HttpStatus httpStatus,OrangeErrorInfo orangeErrorInfo,String errorMessage) throws IOException {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(httpStatus.value());
requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
requestContext.setResponseBody(errorHandlingService.buildErrorResponseBody(
requestContext.getRequest(),orangeErrorInfo,errorMessage));
}
/**
* computes the creation date of the token using the validity duration defined in different environments
* (creation date = expiration date - validity duration) and checks whether this date is earlier that the token's
* last updated date
*
* @param tokenLastUpdated the date where the token was last modified/updated
* @param expiration the token expiration date in seconds
* @return true if the creation date is earlier than last updated date
*/
private boolean isInvalidToken(Date tokenLastUpdated,Long expiration) {
Instant expirationDateTime = Instant.ofEpochSecond(expiration);
Instant tokenLastUpdatedDateTime = tokenLastUpdated.toInstant();
Duration duration = Duration.ofSeconds(validitySeconds);
return expirationDateTime.minus(duration).isBefore(tokenLastUpdatedDateTime.truncatedTo(ChronoUnit.SECONDS));
}
}
@Controller
public class CustomErrorController extends BasicErrorController {
@Autowired
private HttpErrorHandlingService httpErrorHandlingService;
public CustomErrorController(ErrorAttributes errorAttributes,ServerProperties errorProperties,List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes,errorProperties.getError(),errorViewResolvers);
}
@Override
public ResponseEntity<Map<String,Object>> error(HttpServletRequest request) {
Exception ex = ((Exception)request.getAttribute("javax.servlet.error.exception"));
if(Objects.nonNull(ex)) {
if(ex.getCause() instanceof MultipartException)
throw new ComboHttpRequestException("The submitted value for content-type header isn't supported",OrangeErrorInfo.INVALID_HEADER_VALUE);
else if(ex instanceof RequestRejectedException)
throw new ComboHttpRequestException(ex.getMessage(),request);
else if(ex instanceof ZuulException)
if(new RateLimitExceededException().getMessage().contains(ex.getMessage()))
throw new ZuulRateLimitException("The application has made too many calls and has exceeded the rate limit for this service",OrangeErrorInfo.TOO_MANY_REQUESTS);
}
return super.error(request);
}
@ExceptionHandler(ComboHttpRequestException.class)
public ResponseEntity<String> handleError(ComboHttpRequestException ex) {
try {
return ResponseEntity.badRequest()
.contentType(MediaType.APPLICATION_JSON)
.body(httpErrorHandlingService.buildErrorResponseBody(String.valueOf(ex.getRequest().getAttribute("javax.servlet.error.request_uri")),ex.getRequest().getmethod(),ex.getorangeErrorInfo(),ex.getMessage()));
} catch (IOException e) {
e.printstacktrace();
}
return ResponseEntity.badRequest().build();
}
@ExceptionHandler(ZuulRateLimitException.class)
public ResponseEntity<String> handleZuulError(ZuulRateLimitException ex){
try {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(httpErrorHandlingService.buildErrorResponseBody(String.valueOf(ex.getRequest().getAttribute("javax.servlet.error.request_uri")),ex.getMessage()));
} catch (IOException e) {
e.printstacktrace();
}
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}
进入这些过滤器并返回com.netflix.zuul.exception.ZuulException:成功地返回429 TOO_MANY_REQUESTS,但在邮递员中返回500
在添加spring-cloud-zuul-ratelimit之前,它们都工作正常,我按照doc的顺序进行操作 请帮助
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)