DooDung 백엔드에서 오류 로그 구름 관측 보내다
실시간으로 500번째 알림을 받으려면 오류를 Slack에 비동기적으로 보냅니다.
하지만 한 번 봄 몸가치가 보이지 않는다
ContentCachingRequestWrapper(스프링 프레임워크 6.0.6 API)
handleContentOverflow protected void handleContentOverflow(int contentCacheLimit) 콘텐츠 오버플로를 처리하기 위한 템플릿 메서드: 특히 읽고 있는 요청 본문이 지정된 콘텐츠 캐시 제한을 초과합니다.
기본 구현이 비어 있습니다.
docs.spring.io
위와 같이 콘텐츠 캐싱 요청/응답 래퍼사용하여
복사 후 꺼내서 쓰세요.
신청 방법은 매우 간단합니다.
필터에서 은닉처당신은 신청할 수 있습니다
@Component
public class HttpContentCacheFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper wrappingResponse =
new ContentCachingResponseWrapper(response);
chain.doFilter(wrappingRequest, wrappingResponse);
wrappingResponse.copyBodyToResponse();
}
}
그러나 프록시 환경에서는 필터의 순서로 인해 ContentCachingRequestWrapper 캐싱할 때
틀릴 수 있습니다
솔루션을 공유하고 싶습니다.
수정: ImNM의 x-forward 필터 풀 요청으로 인한 ContentCachingRequestWrapper 캐스팅 오류 #267 Gosrock/Du
Synopsis close #266 500번째 범위에서 오류가 발생하면 서버가 Slack 알림을 보내지 않습니까? nginx가 프록시로 실행되고 있기 때문에 x-Forward 헤더가 전달되고 있지만 이 시점에서 ForwardedHeaderFilter가 작동합니다.
github.com
이 글의 내용은 섭외기반으로
색인
1. 문제
2. 개선
2.1 스프링 시큐리티처럼 필터 조정이 안되나요?
2.2 순서대로 조정
1. 문제
final ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request;
Slack에 문자 메시지와 함께 오류 보내기 전역 예외 처리기 존재하다
@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleException(Exception e, HttpServletRequest request)
throws IOException {
final ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request;
500은 상업적으로 취급할 수 없습니다.
예외 캡처 및 처리가 필요할 때
HttpServlet 요청 다시 ContentCachingRequestWrapper로 캐스팅해야합니다
로컬에서 개발할 때 전혀 문제가 없습니다.
하지만 두 서버 nginx ~에 대리인 정해진 환경이지만 언젠가는
java.lang.ClassCastException:
class org.springframework.web.filter.ForwardedHeaderFilter$ForwardedHeaderExtractingRequest
cannot be cast to class org.springframework.web.util.ContentCachingRequestWrapper
위와 같은 오류가 발생합니다.
다시 말해서 전달된 헤더 가져오기 요청 ~처럼 필요하다넘어와서 캐스팅을 못하는 문제다.
포워딩 헤더 필터그리고 트리거 조건을 살펴보았습니다.
// FORWARDED_HEADER_NAMES
FORWARDED_HEADER_NAMES.add("Forwarded");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Host");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Port");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Proto");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Prefix");
FORWARDED_HEADER_NAMES.add("X-Forwarded-Ssl");
FORWARDED_HEADER_NAMES.add("X-Forwarded-For");
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
for (String headerName : FORWARDED_HEADER_NAMES) {
if (request.getHeader(headerName) !
= null) {
return false;
}
}
return true;
}
즉, 위의 헤더가 포함되어 있어야 필터가 작동합니다.
nginx는 어떻습니까?필요하다 업스트림 서버전달할 때 그렇게 작동하려면 어떤 헤더를 전달해야 합니까? 알겠어요.
X-type 외에도 헤더에 remote_addr 및 기타 정보가 포함되어 업스트림으로 전달되는 것을 볼 수 있습니다.
위의 조건 때문입니다.
이것이 바로 ForwardedHeaderFilter가 하는 일입니다.
로컬에서 작동하지 않는 ForwardedHeaderFilter가 작동하기 때문에 캐스팅 오류가 발생하는 것으로 확인되었습니다.
그렇다면 캐스팅 오류가 발생한 이유는 무엇입니까?
대답은 필터의 순서 때문입니다.
내가 그랬어 HttpContentCacheFilter가다 포워딩 헤더 필터 앞에
나는 해결책을 생각해 냈다
포워딩 헤더 필터 필터 HttpContentCacheFilter 앞으로 나오시면
마지막으로 오류 처리기에서 전달된 HttpRequest는 다음과 같습니다.
ContentCachingRequestWrapper 분명히, 입력
2. 개선
2.1 스프링 시큐리티처럼 필터 조정이 안되나요?
당연히… 생각할 수 있을 것 같아요. 저도요.
@RequiredArgsConstructor
@Component
public class FilterConfig
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenFilter jwtTokenFilter;
private final JwtExceptionFilter jwtExceptionFilter;
private final HttpContentCacheFilter httpContentCacheFilter;
@Override
public void configure(HttpSecurity builder) {
builder.addFilterBefore(jwtTokenFilter, BasicAuthenticationFilter.class);
builder.addFilterBefore(jwtExceptionFilter, JwtTokenFilter.class);
// ForwardedHeaderFilter뒤에 놓으면되네!
builder.addFilterBefore(httpContentCacheFilter, ForwardedHeaderFilter.class);
}
}
위와 같이 하시면 안됩니다.
위 사진에서 형광부분 보이시죠? 대리인 필터 프록시지나가면서
Spring Security의 가상 필터가 시작됩니다.
3개의 서블릿 필터 통과 ->
위 그림의 Spring Security 로직 실행 ->
filter4를 반환하여 실행합니다.
위의 코드와 같이 설정하면 Spring Security 내부의 위치만 변경됩니다.
포워딩 헤더 필터서블릿의 필터입니다.
그럼 우리는 다른 방법을 찾아야 합니다.
2.2 주문 신청
서블릿 필터의 순서를 적용하는 방법 @주문하다 주석을 사용하는 한 가지 방법입니다.
@Order(Integer.MAX_VALUE)
@Component
public class HttpContentCacheFilter extends OncePerRequestFilter {
위와 같은 방법으로 순서를 지정하면 끝입니다.
나도 그러지 않을거야..
라스트 오더로 가려고 했는데…아무 일도 없었습니다.
spring-boot의 필터 순서
spring-boot에서 필터 순서를 지정하는 방법은 무엇입니까? Spring Security 필터 뒤에 MDC 필터를 삽입해야 합니다.
나는 거의 모든 것을 시도했지만 내 필터가 항상 먼저옵니다.
이것은 작동하지 않았습니다: @Bean…
stackoverflow.com
그 이유는 위에서 찾을 수 있습니다.
Spring Security나 ForwardedHeaderFilter는 순서를 지정하지 않으므로 마지막에 할당됩니다.
아무것도 지정하지 않으면 컴파일 순서대로 할당되는 것 같지만 임의로…
따라서 순서를 수동으로 다시 정렬해야 합니다.
@Configuration
@RequiredArgsConstructor
@Profile({"prod", "staging", "dev"})
public class ServletFilterConfig implements WebMvcConfigurer {
private final HttpContentCacheFilter httpContentCacheFilter;
private final ForwardedHeaderFilter forwardedHeaderFilter;
@Bean
public FilterRegistrationBean securityFilterChain(
@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
Filter securityFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
registration.setOrder(Integer.MAX_VALUE - 3);
registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
return registration;
}
@Bean
public FilterRegistrationBean setResourceUrlEncodingFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new ResourceUrlEncodingFilter());
registrationBean.setOrder(Integer.MAX_VALUE - 2);
return registrationBean;
}
@Bean
public FilterRegistrationBean setForwardedHeaderFilterOrder() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(forwardedHeaderFilter);
registrationBean.setOrder(Integer.MAX_VALUE - 1);
return registrationBean;
}
@Bean
public FilterRegistrationBean setHttpContentCacheFilterOrder() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(httpContentCacheFilter);
registrationBean.setOrder(Integer.MAX_VALUE);
return registrationBean;
}
}
위의 방법을 사용하여 필터 위치를 변경할 수 있습니다.
그림과 같이 필터가 원하는 위치로 재조정된 것을 볼 수 있습니다.
따라서 프록시 환경에서 콘텐츠 캐싱 필터를 적용하는 방법을 찾아서 적용해봤습니다.
이것을 알아내려고 거의 하루 종일 디버거를 실행한 것 같습니다.
덕분에 스프링 시큐리티 필터와 서블릿 필터가 어떻게 동작하는지 이해할 수 있었습니다.
위 이미지의 숫자 7, 8, 9는 실제로 내가 사용자 정의한 필터입니다.
@Component 어노테이션을 사용하여 빈으로 등록하여 사용하는 컴포넌트입니다.
해당 필터도 Spring Security 필터에 등록되어 사용됩니다.
그러면 두 번 작동하는지 궁금할 수 있습니다.
OnecPerRequestFilter계승해서 사용하고 있으니 문제는 없을 것 같습니다.
이걸 보니 보안 FilterConfig에서 di를 받는 대신 생성자로 생성해야 할까요?나는 의심이
위의 리소스는 다음 저장소에서 참조할 수 있습니다.
GitHub – Gosrock/DuDoong-Backend: 모두를 위한 성능 새 생명, 두둥!
두둥, 모두를 위한 새로운 연기생활 시작!
GitHub에서 계정을 생성하여 Gosrock/DuDoong 백엔드 개발에 기여하십시오.
github.com