OncePerRequestFilter란?

개발새발·2023년 1월 21일
0

spring

목록 보기
19/26
post-custom-banner

SecurityConfig 를 다루다가, 인증필터를 만들어둔 것에서 발견한 게 있었다. 커스텀한 인증필터가 OncePerRequestFilter 를 상속받아서 만들었던데 이 filter에 대해 잘 모르는 것 같아서 정리하게 되었다.

Filter란?

우선, 필터는 웹 어플리케이션에 등록해야 한다. 필터는 요청 스레드가 서블릿 컨테이너에 도착하기전에 시행되는데, 이때 사용자의 요청 정보에 대한 검증과 필요한 데이터가 있다면 추가하거나 변경해버릴 수 있다. 응답에 대한 정보도 변경이 물론 가능한데, 보통은 전역적으로 처리해야하는 인코딩과 보안과 관련해서 수행되게 된다.

Filter 사용

  • 웹 보안 관련 기능 처리
  • 로그인 여부나 권한 검사와 같은 인증 기능
  • 오류 처리
  • 요청 및 응답 관련 로그

Filter가 수행되는 위치

그렇다면 OncePerRequestFilter란?

한 요청에 대해 한번만 실행하는 필터이다. 포워딩이 발생하면 필터 체인이 다시 동작되는데, 인증은 여러번 처리가 불필요하기에 한번만 처리를 할 수 있도록 도와주는 역할을 한다.

OncePerRequestFilter 의 doFilter

보통 OncePerRequestFilter를 상속받은 후 doFilter 메소드를 override하여 사용한다. 최초 실행시에 ServletRequest객체에 자신의 이름과 함께 true값을 함께 세팅해두고, doFilterInternal 메소드로 자신의 기능을 수행한다. 그 후에 리다이렉트로 다시 실행되면 요청 객체에 담아뒀던 이전 수행에 대한 여부를 체크한다.

public abstract class OncePerRequestFilter extends GenericFilterBean {

	/**
	 * Suffix that gets appended to the filter name for the
	 * "already filtered" request attribute.
	 * @see #getAlreadyFilteredAttributeName
	 */
	public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

	/**
	 * This {@code doFilter} implementation stores a request attribute for
	 * "already filtered", proceeding without filtering again if the
	 * attribute is already there.
	 * @see #getAlreadyFilteredAttributeName
	 * @see #shouldNotFilter
	 * @see #doFilterInternal
	 */
	@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;

		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...
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpRequest, httpResponse, filterChain);
			}
			finally {
				// Remove the "already filtered" request attribute for this request.
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}
....

WebSecurityConfigurerAdapter 를 상속받은 securityconfig

스프링시큐리티를 사용하여 로그인/인가 등을 처리하기 위한 방법이다.

  • @EnableWebSecurity가 붙어있으면, 스프링 시큐리티를 구성하는 기본적인 빈(Bean)들을 자동으로 구성해준다.
  • WebSecurityConfigurerAdapter를 상속받으면, 특정 메소드를 오버라이딩하여 좀 더 쉽게 인증처리를 할 수 있다.
@Configurable
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final CutomFilter customFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
   }

cc. https://junhyunny.github.io/spring-boot/once-per-request-filter/, https://ivory-room.tistory.com/19

profile
발새발개
post-custom-banner

0개의 댓글