티스토리 뷰

자바와 스프링에 대한 기본 지식을 기르기 위해 토이 프로젝트를 시작했습니다.

 

토이 프로젝트로 배우는 자바 스프링 [0]. prologue

자바와 스프링에 대한 기본 지식을 기르기 위해 토이 프로젝트를 시작했습니다. 프론트 코드 : https://github.com/laboratory-kkoon9/connector_front GitHub - laboratory-kkoon9/connector_front Contribute to laboratory-kkoon9/co

kkoon9.tistory.com

프론트 코드 : https://github.com/laboratory-kkoon9/connector_front

백엔드 코드 : https://github.com/laboratory-kkoon9/connector_back

배경

filter에 jwt 토큰을 통해서 권한 체크하는 로직(이하 권한 체크 filter)을 구현했습니다.

로그인 api는 jwt 토큰을 얻기 전이라 권한 체크 filter 로직에서 제외시켜야 합니다.

유저 정보 api는 jwt 토큰을 이용해서 유저 정보를 조회하기 때문에 권한 체크 filter 로직에서 포함시켜야 합니다.

두 api는 HTTP Method는 다르지만, api path는 동일한 상태입니다.

  • 로그인 api : [POST] /api/auth
  • 유저 정보 api : [GET] /api/auth

OncePerRequestFilter - shouldNotFilter

OncePerRequestFilter

shouldNotFilter를 설명하기 전에 OncePerRequestFilter에 대해서 Filter와의 차이점을 들면서 간단하게 설명하겠습니다.

기본 Filter는 javax.servlet 패키지에 존재하며 서블릿 API의 일부입니다.

Filter는 보통 HTTP 요청을 가로채서 처리하는 데 사용됩니다. 기본적인 필터는 요청이 필터 체인을 통해 지나갈 때마다 실행됩니다.

이는 중첩된 요청이나 포워딩 된 요청에도 모두 적용됩니다.

doFilter 메서드를 통해서 FilterChain을 구성할 수 있습니다.

OncePerRequestFilter는 org.springframework.web.filter 패키지에 존재하는 추상클래스입니다.

패키지 위치를 보면 알 수 있듯이 서블릿 API가 아닌 스프링 프레임워크에서 제공하는 클래스입니다.

이름에서 알 수 있듯이 HTTP 요청 당 한 번만 실행되도록 보장합니다. 이를 통해 동일한 요청이 여러 필터 체인을 거쳐도 중복 실행되지 않게 할 수 있습니다.

doFilterInternal 메서드를 통해서 FilterChain을 구성할 수 있습니다.

shouldNotFilter

OncePerRequestFilter에서는 특정 요청에 대해 필터링을 건너뛸 수 있도록 shouldNotFilter 메서드를 제공합니다.

이 메서드는 특정 조건을 만족하는 요청에 대해서는 doFilterInternal 메서드가 호출되지 않도록 합니다.

정리해보자면 shouldNotFilter를 사용해서 doFilterInternal 메서드를 호출되지 않게 하려면 true로 리턴되어야 합니다.

package com.connector.global.filter;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, NullPointerException {
        // jwt 토큰을 이용해서 권한 체크 로직

        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        // 로그인 api
        if(로그인 api) return true
        
        return false
    }
}

AntPathRequestMatcher

요구사항을 다시 생각해봅시다.

HTTP Method는 다르지만, api path는 동일한 두 API를 구분할 수 있어야 한다.

AntPathRequestMatcher는 Spring Security에서 URL 패턴 매칭을 쉽게 할 수 있도록 도와주는 클래스입니다. 이는 특히 복잡한 경로 매칭을 단순화하고, 와일드카드를 사용하여 유연한 URL 매칭을 가능하게 합니다.

AntPathRequestMatcher 클래스는 여러 생성자를 제공하는데, pattern만 가지는 생성자뿐만 아니라 http method를 포함하는 생성자도 제공합니다.

public final class AntPathRequestMatcher implements RequestMatcher, RequestVariablesExtractor {

    private static final String MATCH_ALL = "/**";

    private final Matcher matcher;

    private final String pattern;

    private final HttpMethod httpMethod;

    private final boolean caseSensitive;

    private final UrlPathHelper urlPathHelper;

    /**
     * Creates a matcher with the specific pattern which will match all HTTP methods in a
     * case sensitive manner.
     * @param pattern the ant pattern to use for matching
     */
    public AntPathRequestMatcher(String pattern) {
       this(pattern, null);
    }

    /**
     * Creates a matcher with the supplied pattern and HTTP method in a case sensitive
     * manner.
     * @param pattern the ant pattern to use for matching
     * @param httpMethod the HTTP method. The {@code matches} method will return false if
     * the incoming request doesn't have the same method.
     */
    public AntPathRequestMatcher(String pattern, String httpMethod) {
       this(pattern, httpMethod, true);
    }
    // 중략
}

저는 pattern과 httpMethod를 가지는 AntPathRequestMatcher 객체를 만들어서 위 요구사항을 처리해보려고 합니다.

다음은 예시 코드입니다.

package com.connector.global.filter;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;


@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        if (new AntPathRequestMatcher("/api/auth", HttpMethod.POST.toString()).matches(request)) {
            return true;
        }
        return false;
    }
}

AntPathRequestMatcher 객체를 만들어서 matches 메서드를 통해서 검사합니다.

필터를 거칠 때마다 AntPathRequestMatcher 객체가 만들어지므로 다음과 같이 상수로 만드는 방법도 고려해봤습니다.

package com.connector.global.filter;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;


@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
    private static final AntPathRequestMatcher LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/api/auth", HttpMethod.POST.toString());
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        if (LOGIN_PATH_REQUEST_MATCHER.matches(request)) {
            return true;
        }
        return false;
    }
}

정리

오늘은 api path와 http method를 통해서 filter 로직을 타지 않게 하는 방법에 대해 알아봤습니다.

읽어주셔서 감사합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함