[JAVA] 필터

JunWoo An·2023년 11월 15일
0

스파르타코딩클럽

목록 보기
19/46

Filter

Filter는 클라이언트로부터 오는 요청과 응답에 대해 최초/최종 단계의 위치이며 이를 통해 요청과 응답의 정보를 변경하거나 부가적인 기능을 추가할수있다. 주로 범용적으로 처리해야하는 작업들인 로깅 및 보안 처리등에 사용된다. 구체적으로 인증,인가 관련 로직을 처리할수있으며 기존에 Service에서 처리하던 로직들을 비즈니스 로직과 분리하여 관리할수있는 장점이있다.

Filter Chain

Filter는 한개만 존재하는 것이 아니라 여러개의 Filter들을 chain 형식으로 묶어 처리될수있다.
예시로 로깅필터와 인증,인가필터로 나뉠수있다.

로깅필터

package com.sparta.springauth.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@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) 필터의 순서를 지정
@Component Bean으로 수동등록
chain.doFilter(request, response) 다음 필터로 이동

인증 및 인가 처리 필터

@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을 가져와서 구분(인가)

// 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;
}

인증이 필요한 URL의 경우 URL에 함께 넘어온 Cookie목록에서 JWT가 저장된 쿠키를 찾아 해당 JWT에대한 검증 및 사용자데이터 조회를 통해 넘어온 username과 DB에 존재하는 name이 같은지 확인함으로써 인증하여 인증이 완료되면 해당 데이터를 HttpServletRequest객체로 Controller API로 전달한다.

이와같이 필터를 사용하면 기존에 Service에서 진행되던 인증이 필터에서 끝나 인증,인가로직과 비즈니스로직을 분리하여 코드의 가독성 및 유지보수가 용이해지는 장점이있다는것을 알수있다.

profile
도전하는 사람

0개의 댓글