[Spring] OAuth2 + JWT 로그인 : (3) JwtAuthenticationFilter의 역할 누락

1 분 소요

5. 인증 필터 구현

  • JwtAuthenticationFilter를 생성하여 요청 헤더에서 JWT를 추출하고 인증합니다.

018c7cce-782e-4efa-9edf-153d438ae4cf

Troubleshooting

**<문제>**

AuthController를 완성시키고 비지니스 로직이 만들어졌기 때문에 중간에 login 기능을 테스트 해보기 위해 thymeleaf를 사용해서 프론트를 만들고 Controller로 연결하여 실행시킴.

그러나 해당 url에 빈화면이 보임,,

**<문제 원인="">**

  1. JwtAuthenticationFilter의 역할 누락

    JwtAuthenticationFilter는 JWT 토큰을 기반으로 사용자를 인증하는 필터로, 요청 헤더에서 JWT 토큰을 추출하고 이를 검증하여 인증 정보를 설정하는 역할을 합니다. 이 필터가 제대로 동작하지 않으면 Spring Security가 올바르게 인증 및 인가를 처리하지 못하고, 이에 따라 보안 설정이 혼란스러워질 수 있다.

  2. Spring Security의 필터 체인 문제 Spring Security는 필터 체인을 통해 각 요청을 처리합니다. 특정 필터가 제대로 등록되지 않거나, 인증 및 인가가 필요한 요청을 처리하지 못하면 요청이 거부되거나 화면이 로드되지 않을 수 있습니다. 특히, JwtAuthenticationFilter가 없을 때 기본적으로 Security가 모든 요청을 인증 처리해야 한다고 인식할 수 있다. 이 경우 인증되지 않은 요청은 필터를 통과하지 못하고 거부될 수 있다.

    .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
/login/security/JwtAuthenticationFilter.java 코드
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String token = request.getParameter("accessToken");
        // 1. 요청 헤더에서 Authorization 헤더를 추출
//        String token = jwtTokenProvider.resolveToken(request);
        // 2. 토큰이 존재하고 유효할 경우, 사용자 정보를 설정
        if (token != null && jwtTokenProvider.validateToken(token)) {

            if (jwtTokenProvider.isTokenExpired(token)) {
                PrintWriter writer = response.getWriter();
                writer.print("access token expired");

                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }

            // 3. 토큰에서 사용자 정보 추출
            Authentication authentication = jwtTokenProvider.getAuthentication(token);

            // 4. SecurityContext에 인증 정보 설정
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        // 5. 필터 체인에 요청을 넘겨 다음 필터로 처리 진행
        filterChain.doFilter(request, response);
    }
}

<코드 뜯어보기>

  • 토큰에서 추출한 사용자 정보로 Authentication 객체 생성
  • SecurityContext에 인증 정보 저장

엥? 아까 CustomOAuth2UserService에서도 인증된 사용자 정보를 SecurityContext에 저장한다고 하지 않았나? JwtAuthenticationFilter에서도 저장하는건가

두 곳 모두 SecurityContext에 인증 정보를 저장하지만 다른 인증 시나리오에서 동작

  • CustomOAuth2UserService (소셜 로그인)
    • 카카오 로그인 시 최초 인증할 때 실행됨
    • OAuth2 인증 후 JWT 토큰을 클라이언트에게 발급
  • JwtAuthenticationFilter (API 요청)
    • 이후 API 요청때마다 JWT 토큰으로 인증
    • 토큰에서 추출한 정보로 SecurityContext 갱신
1. 소셜 로그인 시나리오:
유저 -> 카카오 로그인 -> CustomOAuth2UserService
                         -> SecurityContext에 인증정보 저장
                         -> JWT 토큰 발급

2. API 요청 시나리오:
유저 -> API 요청 + JWT 토큰 -> JwtAuthenticationFilter
                            -> SecurityContext에 인증정보 저장
                            -> API 처리

이렇게 두 단계로 나누어진 이유는:

  1. 소셜 로그인은 최초 1회만 필요
  2. 이후 요청은 JWT로 간단히 인증 처리
  3. 서버의 무상태성(Stateless) 유지
  4. API 요청의 효율성 향상

카테고리:

업데이트: