JWT 도입 6편 - Logout

민선규·2024년 7월 17일
0

JAVA

목록 보기
24/25
post-thumbnail

JWT

마지막 Logout입니다. 클라이언트에서 로그아웃을 요청했을 때 지금까지 구현된 내용에서는 별도로 처리를 해주어야 하는 필요가 있습니다.

서버에서는 현재 Redis에 저장된 Refresh Token을 삭제해주고 쿠키를 초기화해주는 작업이 필요합니다.

CustomLogoutFilter

로그아웃을 처리하기 위한 CustomLogoutFilter입니다. 필터로 등록하기 위해서 GenericFilterBean을 상속받았으며 각 로직의 설명은 코드에 주석으로 작성하였습니다.

@RequiredArgsConstructor
public class CustomLogoutFilter extends GenericFilterBean {

    private final JwtUtil jwtUtil;
    private final RefreshTokenRepository refreshTokenRepository;
    private static final String LOGOUT_URL = "/logout";
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }

    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    
    	//요청 URI 값을 가져옵니다.
        String requestUri = request.getRequestURI();
        //요청 URI 값이 LOGOUT_URL이 아니라면 다음 필터로 이동합니다.
		if (!requestUri.equals(LOGOUT_URL)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        //요청 메소드 값을 가져옵니다.
        String requestMethod = request.getMethod();
        //요청 메소드 값이 POST가 아니라면 다음 필터로 이동합니다.
        if (!requestMethod.equals("POST")) {
            filterChain.doFilter(request, response);
            return;
        }

		
        String refresh = null;
        //요청에 저장되어 있는 쿠키를 배열 형태로 가져옵니다.
        Cookie[] cookies = request.getCookies();
		//쿠키가 null이면 에러 response를 설정하고 리턴합니다.
        if(cookies == null){
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            PrintWriter writer = response.getWriter();
            writer.write("Refresh Token Null");
            return;
        }

		//쿠키에 저장된 Refresh Token 값을 가져옵니다.
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("refresh")) {
                refresh = cookie.getValue();
                break;
            }
        }

		//Refresh Token의 값이 null이면 에러 response를 설정하고 리턴합니다.
        if (refresh == null) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            PrintWriter writer = response.getWriter();
            writer.write("Refresh Token Null");
            return;
        }

		//Refresh Token가 만료되었는지 확인하고 만료되었다면 에러 response를 설정하고 리턴합니다.
        try {
            jwtUtil.isExpired(refresh);
        } catch (ExpiredJwtException e) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            PrintWriter writer = response.getWriter();
            writer.write("Refresh Token Expired");
            return;
        }

		//Refresh Token의 category 값을 가져옵니다.
        String category = jwtUtil.getCategory(refresh);
        //category가 refresh가 아닌 경우에 에러 response를 설정하고 리턴합니다.
        if (!category.equals("refresh")) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            PrintWriter writer = response.getWriter();
            writer.write("Invalid Refresh Token");
            return;
        }
		
        //Redis에 Refresh Token이 저장되어 있지 않으면 에러 response를 설정하고 리턴합니다.
        if (!refreshTokenRepository.exist(refresh)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            PrintWriter writer = response.getWriter();
            writer.write("Invalid Refresh Token");
            return;
        }

		//기존에 Redis에 저장된 RefreshToken을 삭제합니다.
        refreshTokenRepository.delete(refresh);

		//쿠키를 초기화합니다.
        Cookie cookie = new Cookie("refresh", null);
        cookie.setMaxAge(0);
        cookie.setPath("/");

        response.addCookie(cookie);
        response.setStatus(HttpServletResponse.SC_OK);

        PrintWriter writer = response.getWriter();
        writer.write("Success");
    }
}

SecurityConfig

위에서 작성한 CustomLogoutFilter를 필터에 추가하였습니다.

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtUtil jwtUtil;
    private final RefreshTokenRepository refreshTokenRepository;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    
        //LogoutFilter 추가
        http
                .addFilterBefore(new CustomLogoutFilter(jwtUtil, refreshTokenRepository), LogoutFilter.class);
                
    }
}

로그아웃 테스트의 경우에는 이전 테스트와 거의 유사하기 때문에 생략하겠습니다. 6편으로 JWT를 도입하는 과정을 정리해보았습니다.

다만 프론트에서 Oauth2처리를 하고 보내준 유저 정보를 기준으로 구현했기 때문에 기존의 JWT와는 차이가 있지만 흘러가는 로직은 거의 유사합니다. 글을 읽으면서 이해안가는 부분이나 부족한 부분있으면 댓글 남겨주시면 감사하겠습니다!

0개의 댓글

관련 채용 정보