ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CodeceptJS에서 로그인하지 않고 특정 페이지에 접속하는 경우 인가 정보를 가져오지 못하는 문제 해결 과정
    Today I Learned 2023. 1. 2. 14:48

    문제 상황

    로그인하지 않은 상태로 서버에 등록된 게시물 목록을 확인하는 인수 테스트를 통과하지 못하는 이슈를 확인했다.

     

    분석 및 해결 과정

    클라이언트에서 서버에 API 요청을 보낼 때, 요청 Headers에 Authorization Bearer에 Access Token을 포함해 전달한다. 서버에서 해당 API 요청을 수신하는 Controller의 메서드는 인자로 requestAttribute로 로그인한 사용자의 식별자를 받는다. 이때 requestAttribute의 requierd 속성이 false이기 때문에, 클라이언트에서 Access Token으로 빈 값을 전달하는 경우에는 해당 식별자 인자에는 null 값이 들어있게 되고, 비즈니스 로직에서는 사용자 식별자가 null인 경우 로그인하지 않은 것으로 판단하고 동작을 수행한다.

     

    따라서 이 문제는 클라이언트가 Access Token으로 빈 값을 전달하는 게 아니라 뭔가 잘못된 문자열을 담아서 전달하는 것이 아닌가 싶은 의문점이 들었다. 이 의문점에 확신을 갖게 해준 부분은 인수 테스트에서 추가적으로 로그아웃을 진행하게 했을 때는 정상적으로 로그인하지 않은 상태에 맞게 데이터를 가져는 것을 확인하면서였다. 로그아웃을 진행할 경우 클라이언트의 Local Storage를 명시적으로 빈 문자열로 변경하도록 구조를 작성했기 때문이다.

     

    따라서 Header의 Authorization Bearer에 어떤 데이터가 담겨져 서버에 요청이 이루어지는지 확인하기 위해 Authentication Interceptor가 출력하는 내용에 대해 직접 디버깅을 시도했다.

     

    // interceptors/AuthenticationInterceptor.java
    
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String authorization = request.getHeader("Authorization");
    
        System.out.println("**********");
        System.out.println("authorization: " + authorization);
        System.out.println("**********");
    
        if (authorization == null || !authorization.startsWith("Bearer ")) {
            return true;
        }
    
        String accessToken = authorization.substring("Bearer ".length());
    
        System.out.println("**********");
        System.out.println("accessToken: " + accessToken);
        System.out.println("**********");
    
        try {
            Long userId = jwtUtil.decode(accessToken);
    
            request.setAttribute("userId", userId);
            return true;
        } catch (JWTDecodeException exception) {
            throw new AuthenticationError();
        }
    }
    **********
    authorization: Bearer null
    **********
    **********
    accessToken: null
    **********
    2023-01-02 14:37:31.610  WARN 98068 --- [nio-8000-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [kr.megaptera.smash.exceptions.AuthenticationError: 인가 과정에서 문제가 발생했습니다.]

     

    디버깅을 수행한 결과 Header에 'null'이라는 문자열이 전달되는 것이 확인되었다.

     

    해결 방법으로는 인수 테스트 시 Local Storage를 직접 조작하는 방법과, AuthenticationInterceptor에서 Header에 'null'이 전달된 것을 확인했을 때 requestAttribute에 값을 부여하지 않고 preHandle 동작을 종료하는 방식이 있었다.

     

    인수 테스트에서 Local Storage만을 직접적으로 조작하기 어렵기도 했고, 사용자 관점에서의 동작을 확인한다는 인수 테스트의 의미에도 맞지 않았기 때문에 두 번째 방식을 이용해 문제를 해결했다.

     

    // interceptors/AuthenticationInterceptor.java를 다음과 같이 수정
    
    String authorization = request.getHeader("Authorization");
    
    if (authorization == null || !authorization.startsWith("Bearer ")) {
        return true;
    }
    
    String accessToken = authorization.substring("Bearer ".length());
    
    if (accessToken.equals("null")) {
        return true;
    }
    
    try {
        Long userId = jwtUtil.decode(accessToken);
    
        request.setAttribute("userId", userId);
        return true;
    } catch (JWTDecodeException exception) {
        throw new AuthenticationError();
    }

     

     

     

     

    댓글

Designed by Tistory.