๐Ÿ“Œ ๋„ค์ด๋ฒ„ ๋กœ๊ทธ์ธ ์—ฐ๋™ ํ•ด์ œ (Token Revocation)

My Pale Blue Dotยท2025๋…„ 5์›” 14์ผ
0

SPRING BOOT

๋ชฉ๋ก ๋ณด๊ธฐ
27/40
post-thumbnail

๐Ÿ“… 2025-05-14

๐Ÿ“ ํ•™์Šต ๋‚ด์šฉ

1๏ธโƒฃ Token Revocation์ด๋ž€?

  • OAuth 2.0 ํ‘œ์ค€(RFC 7009)์— ๋”ฐ๋ผ access_token, refresh_token์„ ๋ฌดํšจํ™”(ํ๊ธฐ)ํ•˜๋Š” ๋ฐฉ์‹
  • ๋„ค์ด๋ฒ„์—์„œ๋Š” ์ด ๊ณผ์ •์„ ๋กœ๊ทธ์ธ ์—ฐ๋™ ํ•ด์ œ๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค ํƒˆํ‡ด ์‹œ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœํ•ด์•ผ ํ•จ
  • ์—ฐ๋™ ํ•ด์ œ๊ฐ€ ์„ฑ๊ณตํ•˜๋ฉด:
    • ๊ธฐ์กด access_token, refresh_token์€ ์ฆ‰์‹œ ๋งŒ๋ฃŒ
    • ๋„ค์ด๋ฒ„ "๋‚ด์ •๋ณด > ์—ฐ๊ฒฐ๋œ ์„œ๋น„์Šค ๊ด€๋ฆฌ" ํ•ญ๋ชฉ์—์„œ๋„ ์„œ๋น„์Šค๊ฐ€ ์ œ๊ฑฐ๋จ

2๏ธโƒฃ ์—ฐ๋™ ํ•ด์ œ ์š”์ฒญ ํ๋ฆ„

โœ… RestTemplate์„ ํ™œ์šฉํ•œ ์—ฐ๋™ ํ•ด์ œ ๊ตฌํ˜„

@GetMapping("/naver/unlink")
public void logout(){
    log.info("GET/naver/unlink...");

    // 1. ๋„ค์ด๋ฒ„ ์—ฐ๋™ ํ•ด์ œ ์š”์ฒญ URL
    String url = "<https://nid.naver.com/oauth2.0/token>";

    // 2. ์š”์ฒญ ํ—ค๋” ์„ค์ • (Content-Type ๋ช…์‹œ ํ•„์š”)
    HttpHeaders header = new HttpHeaders();
    header.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    // 3. ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("client_id", NAVER_CLIENT_ID); // ์•ฑ ๋“ฑ๋ก ์‹œ ๋ฐ›์€ Client ID
    params.add("client_secret", NAVER_CLIENT_SECRET); // Client Secret
    params.add("access_token", this.naverTokenResponse.getAccess_token()); // ์œ ํšจํ•œ access_token
    params.add("grant_type", "delete"); // ์š”์ฒญ ํƒ€์ž…์€ ๋ฐ˜๋“œ์‹œ delete

    // 4. ์š”์ฒญ ์—”ํ‹ฐํ‹ฐ ๊ตฌ์„ฑ
    HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, header);

    // 5. ์š”์ฒญ ์ „์†ก ๋ฐ ์‘๋‹ต ์ˆ˜์‹ 
    RestTemplate rt = new RestTemplate();
    ResponseEntity<String> response = rt.exchange(url, HttpMethod.POST, entity, String.class);

    // 6. ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    System.out.println(response.getBody());
    // ์˜ˆ์‹œ ์‘๋‹ต:
    // {"result":"success","access_token":"AAAAOVGk7..."}
}

3๏ธโƒฃ ๋ธŒ๋ผ์šฐ์ € ์„ธ์…˜ ๋กœ๊ทธ์•„์›ƒ ๋ฆฌ๋””๋ ‰์…˜

@GetMapping("/naver/logout")
public String disConnect(){
    log.info("GET/naver/logout...");
    return "redirect:<https://nid.naver.com/nidlogin.logout?returl=https://www.naver.com/>";
}

์œ„ API๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋„ค์ด๋ฒ„ ์„ธ์…˜๋งŒ ๋งŒ๋ฃŒ์‹œํ‚ด
access_token ํ๊ธฐ์™€๋Š” ๋ฌด๊ด€ํ•˜๋ฏ€๋กœ, ๋ณด์•ˆ์„ ์œ„ํ•ด /unlink์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•จ


๐Ÿ”ง ์‚ฌ์šฉ๋œ DTO ํด๋ž˜์Šค

// ์—‘์„ธ์Šค ํ† ํฐ ์‘๋‹ต DTO
@Data
private static class NaverTokenResponse {
    public String access_token;
    public String refresh_token;
    public String token_type;
    public String expires_in;
}

// ์‚ฌ์šฉ์ž ์ •๋ณด ์‘๋‹ต DTO
@Data
private static class Response {
    public String id;
    public String profile_image;
    public String email;
    public String name;
}

@Data
private static class NaverProfileResponse {
    public String resultcode;
    public String message;
    public Response response;
}

๐Ÿ”น ์ถœ๋ ฅ ๊ฒฐ๊ณผ ์˜ˆ์‹œ

{
  "result": "success",
  "access_token": "AAAAOVGk7cIRQXi1hmVeVYXrP..."
}

๐Ÿ”ฅ ์ •๋ฆฌ

  • ๋„ค์ด๋ฒ„ ์—ฐ๋™ ํ•ด์ œ๋Š” access_token ํ๊ธฐ๋ฅผ ์˜๋ฏธํ•จ
  • ๋‹จ์ˆœ ๋กœ๊ทธ์•„์›ƒ๋งŒ์œผ๋กœ๋Š” ์—ฐ๋™์ด ๋Š๊ธฐ์ง€ ์•Š์Œ
  • ์‚ฌ์šฉ์ž ํƒˆํ‡ด ์‹œ์—๋Š” ๋ฐ˜๋“œ์‹œ /unlink๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•จ
  • RestTemplate ์‚ฌ์šฉ ์‹œ ๋ฐ˜๋“œ์‹œ Content-Type: application/x-www-form-urlencoded ์„ค์ • ํ•„์š”
  • ์‘๋‹ต์ด "result": "success"์ผ ๊ฒฝ์šฐ ์ •์ƒ ์ฒ˜๋ฆฌ๋จ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€