SecurityConfig 를 다루다가, 인증필터를 만들어둔 것에서 발견한 게 있었다. 커스텀한 인증필터가
OncePerRequestFilter
를 상속받아서 만들었던데 이 filter에 대해 잘 모르는 것 같아서 정리하게 되었다.
우선, 필터는 웹 어플리케이션에 등록해야 한다. 필터는 요청 스레드가 서블릿 컨테이너에 도착하기전에 시행되는데, 이때 사용자의 요청 정보에 대한 검증과 필요한 데이터가 있다면 추가하거나 변경해버릴 수 있다. 응답에 대한 정보도 변경이 물론 가능한데, 보통은 전역적으로 처리해야하는 인코딩과 보안과 관련해서 수행되게 된다.
한 요청에 대해 한번만 실행하는 필터이다. 포워딩이 발생하면 필터 체인이 다시 동작되는데, 인증은 여러번 처리가 불필요하기에 한번만 처리를 할 수 있도록 도와주는 역할을 한다.
보통 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);
}
}
}
....
스프링시큐리티를 사용하여 로그인/인가 등을 처리하기 위한 방법이다.
@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