본문 바로가기

개인 프로젝트

로그아웃 (CustomLogoutFilter)

로그아웃 기능

로그아웃 버튼 클릭시

  • 프론트엔드측: 로컬 스토리지에 존재하는 Access 토큰 삭제 및 Axios Api Client를 이용해서 서버측 로그아웃 경로로 Refresh 토큰 전송
  • 백엔드측: 로그아웃 로직을 추가하여 Refresh 토큰을 받아 쿠키 초기화(null) 후 Refresh DB에서 해당 Refresh 토큰을 삭제 (모든 계정에서 로그아웃 구현시 username 기반으로 모든 Refresh 토큰 삭제)

 

백엔드에서 로그아웃 수행 작업

  1. DB에 저장하고 있는 Refresh 토큰 삭제
  2. Refresh 토큰 쿠키 null로 변경

 

public class CustomLogoutFilter extends GenericFilterBean {

    private final JWTUtil jwtUtil;
    private final RefreshRepository refreshRepository;

    public CustomLogoutFilter(JWTUtil jwtUtil, RefreshRepository refreshRepository) {

        this.jwtUtil = jwtUtil;
        this.refreshRepository = refreshRepository;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        doFilter((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse, filterChain);
    }

    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {

        //path and method verify
        String requestUri = request.getRequestURI();
        if (!requestUri.matches("^\\/api\\/account\\/logout$")) {

            filterChain.doFilter(request, response);
            return;
        }
        String requestMethod = request.getMethod();
        if (!requestMethod.equals("POST")) {

            filterChain.doFilter(request, response);
            return;
        }

        //get refresh token
        String refresh = null;
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {

            if (cookie.getName().equals("refresh")) {

                refresh = cookie.getValue();
            }
        }

        //refresh null check
        if (refresh == null) {

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

        //expired check - 만료된 상태는 이미 로그아웃된 상태로 추가적으로 로그아웃 진행하지 않음
        try {
            jwtUtil.isExpired(refresh);
        } catch (ExpiredJwtException e) {

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

        // 토큰이 refresh인지 확인 (발급시 페이로드에 명시)
        String category = jwtUtil.getCategory(refresh);
        if (!category.equals("refresh")) {

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

        //DB에 저장되어 있는지 확인
        Boolean isExist = refreshRepository.existsByRefresh(refresh);
        if (!isExist) { //없다면 - 이미 로그아웃된 상태

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

        //로그아웃 진행
        //Refresh 토큰 DB에서 제거
        refreshRepository.deleteByRefresh(refresh);

        //Refresh 토큰 Cookie 값 0 - 초기화
        Cookie cookie = new Cookie("refresh", null);
        cookie.setMaxAge(0);
        cookie.setPath("/");

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

 

 

위 코드를 적용해서 POSTMAN에서 작동해봤는데 

deleteByRefresh까진 실행이 되는데 쿠키의 Refresh 토큰이 사라지지 않는 것이다...... 

 

문제는 쿠키의 보안 속성이었다..........

 

쿠키에 Secure 또는 HttpOnly 속성이 설정되어 있으면 삭제할 때도 동일한 속성을 사용해야 한다. 예를 들어서 쿠키가 HttpOnly로 설정되어 있으면, 쿠키를 삭제할 떄도 HttpOnly 속성을 포함시켜야 한다.

cookie.setSecure(true); // 만약 원래 쿠키가 Secure로 설정되었다면
cookie.setHttpOnly(true); // 만약 원래 쿠키가 HttpOnly로 설정되었다면

 

라고 설정하니 정상적으로 로그아웃 됐다...

 

 


 

 

나는 어떻게 할거냐면~~~~~~~~~~~~~

 

로그인

 

Refresh Token은 쿠키에 담아서

Access Token은 헤더에 담아서 전달한다. 각각 refresh, access라는 이름으로

 

그리고 두 token을 전달받은 프론트엔드는

 

Refresh Token을 쿠키에

Access Token을 로컬 스토리지 또는 세션 스토리지에 담는다. (자동 로그인을 위해 로컬 스토리지에 담을 예정)

 

Access Token을 담아서 Api 요청시 

만약 Access Token이 만료되면

프론트엔드 측에서 Refresh Token을 보내면 서버 측에서 Refresh Token과 Access Token을 다시 발급해 프론트엔드 측으로 반환한다.

 

기존 Refresh Token은 DB에서 삭제하고 새로운 Refresh Token을 저장한다.

 

 

로그아웃

 

로그아웃 버튼 클릭시

프론트엔드 측에서 로컬 스토리지에 존재하는 Access Token 삭제하고

Axios Api Client를 이용해서 서버 측 로그아웃 경로로 Refresh Token을 전송한다.

 

서버 측에서 Refresh Token을 받으면 쿠키를 null로 초기화하고 

DB에서 해당 Refresh Token을 삭제한다.

 

만약 모든 계정에서 로그아웃 구현시 email기반으로 모든 Refresh 토큰을 삭제한다. (그렇다면 DB에서 email Column에 unique 속성을 삭제해야겠지)

 

'개인 프로젝트' 카테고리의 다른 글

API 명세서  (0) 2024.08.30
Axios Interceptors  (0) 2024.08.17
Refresh Token  (0) 2024.08.13
LoginFilter, JWTFilter, Spring Security 인증 과정  (0) 2024.08.07
SecurityConfig 수정 전/후  (0) 2024.08.02