Spring Interceptor 와 JWT Token 페이지 이동 문제

안녕하세요.

오늘은 Jwt token 을 이용하여 로그인 기능을 구현하고, Authentication(인증) Authorization(인가) 기능을 spring interceptor 를 이용하여 처리를 해줄려고 하였습니다.
그러나 페이지 이동과 interceptor에서 문제가 발생하였고 이를 어떻게 해결하였는지 한번 적어보려고 합니다.

기본적인 동작 구조를 간단히 설명해 드리면

  1. 로그인을 하기 위해 아이디와 비밀번호 체크는 api 방식으로 처리해주었습니다.
    javascript에서 fetch 함수를 이용하여 비동기로 통신하게 됩니다.

  2. 유효한 아이디와 비밀번호를 입력했다면 올바른 http status 와 jwt token을 생성하고 return 시켜줍니다. 만약 틀린 정보를 입력했으면 null을 return 해주게끔 했습니다.

  3. 올바른 token을 return 받고 난 후에는 로그인이 되었으니 로그인이 된 상태의 페이지로 이동하게끔 해주었습니다. 이때, 페이지 이동은 location.href 를 이용합니다.

바로 3번에서 문제가 발생했습니다.

그 이유는 Interceptor 에서는 "/","/login" 만 빼고 전부 인증과 인가의 과정을 거쳐야 하는데, 단순히 location.href를 이용하여 페이지 이동을 하려고 하니 token을 넘겨주지 못하는 GET 방식을 이용하여서 무조건 null을 뱉어낼 수 밖에 없는 구조가 되버렸습니다.

const response = await fetchData("/api/member/login",header);
    const token = response.token;
    if(token){
      sessionStorage.setItem("Authorization",`Bearer ${token}`);
      location.href = location.origin+"/home";
    }

이런식으로 말이죠. 이 문제를 어떻게 해결해야 될까 고민을 정말 많이 하던 끝에 선택한 방법은 form-data 방식으로 서버로 token을 함께 보내주는 것이었습니다.

//login.js
const loginOperation = async (event) =>{
  event.preventDefault();
  const loginDto={
    userId: document.getElementById("id").value,
    password: document.getElementById("password").value,
  }
  const header ={
    method: 'POST',
    body: JSON.stringify(loginDto),
  }
  try{
    const response = await fetchData("/api/member/login",header);
    const token = response.token;
    if(token){
      sessionStorage.setItem("Authorization",`Bearer ${token}`);

      const form = document.createElement('form');
      form.action=`/home`;
      form.method=`POST`;
      form.innerHTML = "<input name='Authorization' value='" + `Bearer ` + token + "'>";

      document.body.append(form);

      form.submit();
    }
  }catch(e){
    alert(e);
    location.reload();
  }
};

위에 소스가 바로 로그인 할때 호출되는 login.js 파일입니다.
보시면 createElement 함수를 이용하여 동적form을 생성하였습니다. 그리고 actionmethod를 정의해준 다음 innerHTML을 이용하여 valuetoken 을 넣어주었습니다.

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        String requestURI = request.getRequestURI();
        String token = requestURI.equals("/home")?request.getParameter("Authorization"):request.getHeader("Authorization");
        if(token==null){
            response.sendRedirect("/login");
            throw new IllegalArgumentException("로그인 먼저 해주세요.");
        }
        if(!jwtTokenProvider.validateToken(token.substring(7))){
            throw new IllegalArgumentException("유효하지 않은 토큰");
        }
        return true;
    }

Interceptorprehandle 함수입니다. 로그인을 성공했으면 자동으로 /home 으로 가게끔 해주었기 때문에 삼항자연산으로 토큰을 가져오는 로직을 나누었습니다.

아쉬운 점

이렇게까지 밖에 할 수 없나??라는 아쉬운 부분들이 너무 크게 남습니다. fetch함수로는 비동기 통신으로 데이터를 request response 하는 구조기 때문에 페이지 이동에는 적합하지 못하고, 결국 Controller 에서 String을 return 시켜 페이지 이동하는 방법 밖에 없을텐데 참...어려운 문제였습니다.

이런 방법을 택하다보니 페이지 이동시의 로직을 전부 form 으로 바꿔줘야 하는 문제점이 발생했으며 이는 제가 원하던 방식이 아니어서 결국 다시 원점으로 돌아가버렸습니다.

혹시나 더 좋은 방법이 있다면 적극적으로 댓글로 의견을 남겨주시면 감사하겠습니다.

좋은 웹페이지 즐겨찾기