Spring

Spring의 Filter 이해하기 및 구현하기

waVwe 2024. 8. 16. 19:12
728x90
반응형

 

 

Filter

 

Filter란 Web 애플리케이션에서 관리되는 영엽으로 Client로 부터 오는 요청과 응답에 대해 최초/최종 단계의 위치이며 이를 통해 요청과 응답의 정보를 변경하거나 부가적인 기능을 추가 할 수 있다.

 

 

주로 범용적으로 처리해야 하는 작업들, 예를 들어 로깅 및 보안 처리, 인증, 인가 등에 활용 한다. Filter를 사용하면 인증/인가와 관련된 로직을 비즈니스 로직과 분리하여 관리 할 수 있다는 장점이 있다.

 

Filter Chain

 

 

Filter는 한 개만 존재하는 것이 아니라 이렇게 여러 개가 Chain 형식으로 묶여서 처리될 수 있다.

 

📍 요청 URL의 인가 처리 및 인증 처리를 진행할 수 있는 Filter 구현 + 요청 URL을 로깅해주는 Filter 구현

 

Request URL Logging

@Slf4j(topic = "LoggingFilter")
@Component
@Order(1)
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 전처리
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();
        log.info(url);

        chain.doFilter(request, response); // 다음 Filter 로 이동

        // 후처리
        log.info("비즈니스 로직 완료");
    }
}

 

@Order(1) 로 필터의 순서를 지정한다.

chain.doFilter(request, response) : 다음 필터로 이동 시킨다.

log.info("비즈니스 로직 완료") : 작업이 다 완료된 후 Client에 응답 전 로그가 작성된 것을 확인 할 수 있다.

 

AuthFilter : 인증 및 인가 처리 필터

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
public class AuthFilter implements Filter {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        if (StringUtils.hasText(url) &&
                (url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))
        ) {
            // 회원가입, 로그인 관련 API 는 인증 필요없이 요청 진행
            chain.doFilter(request, response); // 다음 Filter 로 이동
        } else {
            // 나머지 API 요청은 인증 처리 진행
            // 토큰 확인
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);

            if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
                // JWT 토큰 substring
                String token = jwtUtil.substringToken(tokenValue);

                // 토큰 검증
                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                // 토큰에서 사용자 정보 가져오기
                Claims info = jwtUtil.getUserInfoFromToken(token);

                User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
                        new NullPointerException("Not Found User")
                );

                request.setAttribute("user", user);
                chain.doFilter(request, response); // 다음 Filter 로 이동
            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }

}

 

httpServletRequest.getRequestURI( ) : 요청 URL을 가져와서 구분한다. (인가)

"/api/user", "/css", "/js"로 시작하는 URL은 인증 처리에서 제외 시킴.

jwtUtil.getTokenFromRequest(httpServletRequest); : httpServletRequest에서 Cookie 목록을 가져와 JWT가 저장된 Cookie 찾기.

 

JwtUtil에 getTokenFromRequest 구현하기

// HttpServletRequest 에서 Cookie Value : JWT 가져오기
public String getTokenFromRequest(HttpServletRequest req) {
    Cookie[] cookies = req.getCookies();
    if(cookies != null) {
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(AUTHORIZATION_HEADER)) {
                try {
                    return URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode 되어 넘어간 Value 다시 Decode
                } catch (UnsupportedEncodingException e) {
                    return null;
                }
            }
        }
    }
    return null;
}

 

tokenValue가 존재하면 토큰 파싱, 검증을 진행하고 사용자 정보를 가져온다. 가져온 사용자 username을 사용해서 DB에 사용자가 존재하는지 확인하고 존재하면 인증이 완료 된 것. 사용자 정보가 필요한 Controller API에 인증 완료 된 User 객체 전달.

 

JwtUtil 관련 게시글은 이전 포스팅 참조 👇🏻

2024.08.16 - [Spring] - Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지

 

Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지

쿠키와 세션 쿠키와 세션 모두 HTTP에 상태 정보를 유지하기 위해 사용된다. 이를 통해 서버는 클라이언트 별로 인증 및 인가를 할 수 있다.사용 예시 > 사이트 팝업의 "오늘 다시 보지 않기" 정보

tildacoderecorder.tistory.com

 

 

728x90
반응형