프로젝트/SUFY

[도착 알리미 SUFY] 3. 카카오 로그인 구현 및 jwt토큰 (2)

kim-dev 2024. 2. 6. 20:44
반응형

 

(1)에 이어서, 이번에는 클라이언트에서 받은 토큰을 검증하는 로직을 만들어 보겠다.

 

사실 이걸 어떻게 구현해야 할 지 되게 고민이 많았는데...
웹페이지를 이동할 때마다 GET요청을 보내서 토큰의 만료 여부를 확인하는 방식으로 짜려고 한다.
훨씬 더 효율적인 방법이 있을 것 같아서 카카오 API 설명을 열심히 둘러봤는데 나는 모르겠더라....ㅜㅜ


일단 토큰들과 만료 시간을 헤더로 받는 GET 컨트롤러를 만들어 주었다.

< LoginApiController.java >

@GetMapping("/tokenVerify")
public int tokenVerify(@RequestHeader("Authorization") String accessToken,
                       @RequestHeader("Refresh-Token") String refreshToken,
                       @RequestHeader("accessExpire") String accessExpire,
                       @RequestHeader("refreshExpire") String refreshExpire,
                       HttpServletResponse response) throws Exception {
    int result = loginService.tokenVerify(response, accessToken, refreshToken, Long.parseLong(accessExpire), Long.parseLong(refreshExpire));

    return result;
}
< LoginService.java >

@Transactional
public int tokenVerify(HttpServletResponse resp, String accessToken, String refreshToken, long accessExpire, long refreshExpire) throws Exception {
    if (!accessToken.startsWith("Bearer ") || accessToken == null) { // 잘못된 액세스 토큰
        return -1;
    }

    Date now = new Date();
    Date accessExpireDate = new Date(accessExpire);
    Date refreshExpireDate = new Date(refreshExpire);

    if (now.after(accessExpireDate)) { // 액세스 토큰 만료
        if (now.before(refreshExpireDate)) { // 리프레시는 유효할 경우
            // 카카오 uri에 요청을 보내 액세스 토큰을 재발급 받는다.
            RestTemplate rt = new RestTemplate();

            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("grant_type", "refresh_token");
            params.add("client_id", HiddenData.kakaoRestApi);
            params.add("refresh_token", refreshToken.replace("Bearer ", ""));

            HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(params, headers);

            ResponseEntity<String> response = rt.exchange(
                    "https://kauth.kakao.com/oauth/token",
                    HttpMethod.POST,
                    kakaoTokenRequest,
                    String.class
            );

            String parsedRespString = response.getBody();
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode parsedResp = objectMapper.readTree(parsedRespString);

            resp.addHeader("ReissuedToken", "Bearer " + parsedResp.get("access_token").asText());
            resp.addHeader("reissuedExpire", Integer.toString(parsedResp.get("expires_in").asInt() * 1000));

            return 2;
        } else { // 리프레시도 만료
            return -1;
        }
    } else { // 액세스 토큰이 유효할 경우
        return 1; // 정상적으로 동작.
    }
}

이렇게 토큰을 검증(?)하는 로직을 짰다.

액세스 토큰의 만료 시각을 받아서 현재 시각이 만료시각을 지났다면, 리프레시 토큰의 만료 여부를 확인한다.
리프레시 토큰이 만료되지 않았다면 https://kauth.kakao.com/oauth/token로 POST요청을 보내 액세스 토큰을 갱신한다. 

리프레시 토큰도 만료되었다면? -1을 반환해서 클라이언트 측에서 로그인 창으로 리다이렉팅 한다.

 

그렇다면 클라이언트 측에서는 ?

< tokenVerify.jsp >

<script>
    const Authorization = localStorage.getItem('Authorization');
    const Refresh_Token = localStorage.getItem('Refresh-Token');
    const accessExpire = localStorage.getItem('accessExpire');
    const refreshExpire = localStorage.getItem('refreshExpire');

    // 로그인 되지 않았다면 로그인 창으로 팅기게 한다.
    if (Authorization === null || Refresh_Token === null || accessExpire === null || refreshExpire === null) {
        location.href = "/login";
    }

    $.ajax({
        type: "GET",
        url: "/tokenVerify",
        headers: {'Authorization': Authorization,
            'Refresh-Token': Refresh_Token,
            'accessExpire': accessExpire,
            'refreshExpire': refreshExpire,},
        contentType: "application/json; charset=utf-8"
    }).done(function(resp, status, xhr) {
        if (resp === -1) { // 비정상적인 토큰
            alert("세션이 만료되어 로그인 페이지로 이동합니다.");

            localStorage.removeItem('Authorization');
            localStorage.removeItem('Refresh-Token');
            localStorage.removeItem('accessExpire');
            localStorage.removeItem('refreshExpire');

            location.href = "/login"
        } else if (resp === 2) {
            reissuedtoken = xhr.getResponseHeader('ReissuedToken');
            reissuedExpire = xhr.getResponseHeader('reissuedExpire')

            localStorage.removeItem('Authorization');
            localStorage.removeItem('accessExpire');

            localStorage.setItem('Authorization', reissuedtoken);
            localStorage.setItem('accessExpire', reissuedExpire);
        }
    }).fail(function(error) {
        alert("에러 발생 (콘솔 확인)");
        console.log(error);
    });
</script>

이렇게 짰다.
컨트롤러에서 -1을 반환받으면, 로그인 페이지로 팅기게 한다.
2를 반환받으면 액세스 토큰을 갱신한다.
1을 반환받는 경우는 정상적으로 검증된 경우이므로 그냥 진행시킨다.

이 tokenVerify.jsp를 앞으로 만들 모든 jsp 파일 최상단에 import시키면 될 것!


 

 

#29 OAuth2.0과 카카오 연동 로그인2 (로그인 서비스 완료)

AccessToken을 통해 카카오 회원정보 가져오기 이전까지 code를 받는 인증처리 완료와 AccessToken을 받아 권한을 부여받는 과정까지 했다. 이번 시간엔 AccessToken으로 카카오쪽으로 카카오로 로그인한

daramgda.tistory.com

 

[Spring] 스프링으로 OAuth2 로그인 구현하기3 - 카카오

1. 카카오 API 서비스 등록 1) 카카오 Devlopes 사이트로 이동한다. 링크 Kakao Developers 카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능

loosie.tistory.com

 

 

velog

 

velog.io

 

 

velog

 

velog.io