Access 토큰 필터: JWTFilter

원승현·2024년 7월 18일

Spring Security

목록 보기
15/17
post-thumbnail

프론트의 API Client로 서버측에 요청을 보낸 후 데이터를 획득한다. 이때 권한이 필요한 경우 Access 토큰을 요청 헤더에 첨부하는데 Access 토큰 검증은 서버측 JWTFilter에 의해 진행된다. 이때 Access 토큰이 만료된 경우 특정한 상태 코드 및 메시지를 응답해야 한다.

package com.example.securityjwt.jwt;

import com.example.securityjwt.dto.CustomUserDetails;
import com.example.securityjwt.entity.UserEntity;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;

@RequiredArgsConstructor
public class JWTFilter extends OncePerRequestFilter {

    private final JWTUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 헤더에서 access키에 담긴 토큰을 꺼냄
        String accessToken = request.getHeader("access");

        // 토큰이 없다면 다음 필터로 넘김
        if (accessToken == null) {

            filterChain.doFilter(request, response);

            return;
        }

        // 토큰 만료 여부 확인, 만료시 다음 필터로 넘기지 않음
        try {
            jwtUtil.isExpired(accessToken);
        } catch (ExpiredJwtException e) {

            //response body
            PrintWriter writer = response.getWriter();
            writer.print("access token expired");

            //response status code
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }

        // 토큰이 access인지 확인 (발급시 페이로드에 명시)
        String category = jwtUtil.getCategory(accessToken);

        if (!category.equals("access")) {

            //response body
            PrintWriter writer = response.getWriter();
            writer.print("invalid access token");

            //response status code
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }

        // username, role 값을 획득
        String username = jwtUtil.getUsername(accessToken);
        String role = jwtUtil.getRole(accessToken);

        UserEntity userEntity = new UserEntity();
        userEntity.setUsername(username);
        userEntity.setRole(role);
        CustomUserDetails customUserDetails = new CustomUserDetails(userEntity);

        Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authToken);

        filterChain.doFilter(request, response);

    }
}

굳이 String accessToken = request.getHeader("access");로 가져오는데 if문으로 category를 검사하는 이유는 다음과 같다.

  • 토큰의 용도가 명시되지 않으면, 잘못된 토큰을 사용하여 접근을 시도할 수 있습니다. 예를 들어, "refresh" 토큰을 "access" 토큰으로 사용하려는 시도가 있을 수 있습니다.
  • 헤더에 포함된 토큰이 실제로 "access" 토큰인지 확인함으로써, 토큰의 용도가 올바른지 확인하는 것입니다.
참고자료: 개발자 유미 스프링 JWT 심화 

0개의 댓글