기록하기

ExceptionHandlerFilter 적용(2) 본문

Server/Spring Boot

ExceptionHandlerFilter 적용(2)

jjungdev 2022. 7. 3. 16:29

이전 글에 이어서 다음 flow 를 기록해보려고 한다.

 

1. Security Configuration 적용 및 flow 설계

2. Spring Security + JWT + Redis 설정 및 진행 과정

3. ExceptionHandlerFilter 설계

 

Spring Security + JWT + Redis 설정 및 진행 과정

JwtAuthenticationFilter 에서 토큰의 유효성을 검증하는 역할을 하며 여기서 Exception 이 발생하게 될 경우 ExceptionHandlerFilter 가 이를 처리하도록 설계했다.

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisRepository redisRepository;

    @Autowired
    private UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String accessToken = getToken(request);
        if (accessToken != null) {
            Long mbId = tokenService.getMbId(accessToken);
            checkLogout(mbId);
            if (mbId != null) {
                UserDetail userDetail = userService.loadUserByMbId(mbId);
                validateAccessToken(accessToken, userDetail);
                processSecurity(request, userDetail);
            }
        }
        filterChain.doFilter(request, response);
    }

    private String getToken(HttpServletRequest request) {
        String tokenHeader = request.getHeader(AUTHORIZATION);
        if (StringUtils.hasText(tokenHeader) && tokenHeader.startsWith(TOKEN_TYPE)) {
            return tokenHeader.substring(TOKEN_START_INDEX);
        }
        return null;
    }

    private void checkLogout(Long mbId) {
        if (!redisRepository.existsById(mbId)) {
            throw new IllegalArgumentException("이미 로그아웃된 회원입니다.");
        }
    }

    private void validateAccessToken(String accessToken, UserDetail userDetail) {
        if (!tokenService.validateToken(accessToken, userDetail)) {
            throw new IllegalArgumentException("토큰 검증 실패");
        }
    }

    private void processSecurity(HttpServletRequest request, UserDetail userDetail) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetail, "", userDetail.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}

먼저 JwtAuthenticationFilter 에서는 다음과 같은 절차를 따른다.

1) Redis 에서 토큰을 찾으려고 할 때 데이터가 없으면 이미 로그아웃을 한 유저로 처리한다. -> IllegalArgumentException 발생

2) UserDetailsService 의 구현체인 커스텀 UserService 에서 유저 정보를 가져오게 되는데 이때 DB 에 한 번 접근을 하도록 처리했다. 이때 Member 정보가 없으면 Exception 이 발생한다. -> NotFoundMemberException 발생

3) 토큰의 유효성을 검증하게 되며 이때 TokenService 에서 토큰을 resolve 하게 되는데 이 과정에서도 토큰에 문제가 발생하게 되면 Exception 이 발생하게 된다. -> IllegalArgumentException 발생

 

즉 정리를 하자면, Spring Security 에서 시큐리티 검증이 필요한 API 의 경우에는 JwtAuthenticationFilter 에서 토큰 검증 과정을 거치게 되는데, 이 과정에서 발생하는 Exception 의 경우에는 ExceptionHandlerFilter 에서 처리하게 되는 것이다. 또한 이 설정은 SecurityConfig 에서 설정을 해주었기에 가능하다. 

 

마지막 기록에서는 간단하게 ExceptionHandlerFilter 에 대해서 설명을 하도록 하겠다.