๐Ÿ’ Spring/Spring Security

[Spring Security] ์Šคํ”„๋ง ๋ถ€ํŠธ OAuth2๋ฅผ ์ด์šฉํ•œ ์นด์นด์˜ค ๊ณ„์ • ๋กœ๊ทธ์ธ (์ง์ ‘ ๊ตฌํ˜„)

iseunghan 2021. 4. 29. 18:12
๋ฐ˜์‘ํ˜•

์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ์นด์นด์˜ค ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. :D

์ฐธ๊ณ  :

  • ๊ตฌ๊ธ€ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.
  • ๋„ค์ด๋ฒ„ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธ์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.
  • ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ํ•˜๊ธฐ์œ„ํ•ด ํ•„์š”ํ•œ API ํ‚ค ๋ฐœ๊ธ‰์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ๊ตฌํ˜„ํ•˜๊ธฐ

์ผ๋‹จ ์นด์นด์˜ค์—์„œ ์ œ๊ณตํ•˜๋Š” ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์นด์นด์˜ค์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฒ„ํŠผ ์ด๋ฏธ์ง€

 

์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด ๋ฐ›์•˜์œผ๋ฉด, ์ด์ œ ๋ฒ„ํŠผ์— ๋งํฌ๋ฅผ ๋‹ฌ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

Request ์š”์ฒญ ๋งํฌ

{REST_API_KEY} ๋ถ€๋ถ„์—๋Š” ์•ฑ ์ƒ์„ฑ ์‹œ ๋ฐœ๊ธ‰๋ฐ›์€ REST API ํ‚ค๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋˜๊ณ , ๋‚˜๋จธ์ง€๋„ ์•„๋ž˜ ํ‘œ๋ฅผ ๋ณด๊ณ  ์•Œ๋งž๊ฒŒ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

URL์„ ์•„๊นŒ ๋ฐ›์€ ์ด๋ฏธ์ง€์™€ ํ•จ๊ป˜ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. (๋ฐœ๊ธ‰ ๋ฐ›์œผ์‹  client_id๋ž‘ uri๋Š” ์ง์ ‘ ๋„ฃ์œผ์…”์•ผํ•ฉ๋‹ˆ๋‹ค.)

<a href="https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code"><img src="/image/kakao_login_medium.png"></a>

๊ทธ๋ฆฌ๊ณ  ์‹คํ–‰ํ•ด๋ณด๋ฉด,

์ด๋ ‡๊ฒŒ ์นด์นด์˜ค ์ •๋ณด ๋™์˜ ํ™”๋ฉด์ด ๋œจ๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๊ฑด ์ด๋ฏธ ๋กœ๊ทธ์ธ์„ ํ•œ ์ƒํƒœ๋ผ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

๋กœ๊ทธ์•„์›ƒ์„ ํ•ด๋ณด๊ณ  ์‹คํ–‰ํ•ด๋ณด๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

์ด๋ ‡๊ฒŒ ๋กœ๊ทธ์ธ์„ ํ•˜๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ์„ ํ•˜๊ณ  [๋™์˜ํ•˜๊ณ  ๊ณ„์†ํ•˜๊ธฐ] ๋ฅผ ํด๋ฆญํ•œ๋‹ค๋ฉด? 

์šฐ๋ฆฌ๊ฐ€ ์„ค์ •ํ•œ Redirect URI๋กœ ์‘๋‹ต์ด ์˜ค๊ฒŒ ๋˜๋Š”๋ฐ, ๋’ค์— code๋ฅผ ๊ฐ€์ง€๊ณ  ์‘๋‹ต์ด ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์ •์ƒ์ ์œผ๋กœ ์ธ์ฆ์ด ๋๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. (404 ์—๋Ÿฌ๊ฐ€ ๋œจ๋Š” ์ด์œ ๋Š” ํ•ด๋‹น url ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.)

 

์ปจํŠธ๋กค๋Ÿฌ ์ž‘์„ฑ

MemberOauth2Controller๋ผ๊ณ  ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

@RestController
@RequestMapping(value = "/login/oauth2/code")
public class MemberOauth2Controller {

    @GetMapping(value = "/kakao")
    public String kakaoOauthRedirect(@RequestParam String code) {
        return "์นด์นด์˜ค ๋กœ๊ทธ์ธ ์ธ์ฆ ์™„๋ฃŒ, code : " + code;
    }
}

@RequestParam์€ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์ด ๊ธฐ๋ณธ ํƒ€์ž…์ด๊ฑฐ๋‚˜ ์ฟผ๋ฆฌ ์ŠคํŠธ๋ง์˜ ๊ธฐ๋ณธ ํƒ€์ž…์ธ String์ธ ๊ฒฝ์šฐ ์–ด๋…ธํ…Œ์ด์…˜ ์ƒ๋žต ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

Response :

http://localhost:8080/login/oauth2/code/kakao?code={AUTHORIZE_CODE}

์„ฑ๊ณต์ ์œผ๋กœ ์ธ๊ฐ€ ์ฝ”๋“œ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

 

์—‘์„ธ์Šค ํ† ํฐ ๋ฐœ๊ธ‰ ๋ฐ›๊ธฐ

์ด์ „์— ๋ฐ›์•˜๋˜ ์ธ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ๊ณ„์ • ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” Access Token์„ ๋ฐ›์•„ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์œ„์˜ ๋ฌธ์„œ์ฒ˜๋Ÿผ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

Request:

// ์นด์นด์˜ค์— POST๋ฐฉ์‹์œผ๋กœ key=value ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•จ. RestTemplate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์š”์ฒญ์„ ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
RestTemplate rt = new RestTemplate();

// HttpHeader ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

// HttpBody ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
	params.add("grant_type", "authorization_code");
	params.add("client_id", "{client_id}");
	params.add("redirect_uri", "http://localhost:8080/login/oauth2/code/kakao");
	params.add("code", code);
	params.add("client_secret", "{secret_code}");

// HttpHeader์™€ HttpBody๋ฅผ HttpEntity์— ๋‹ด๊ธฐ (why? rt.exchange์—์„œ HttpEntity๊ฐ์ฒด๋ฅผ ๋ฐ›๊ฒŒ ๋˜์–ด์žˆ๋‹ค.)
HttpEntity<MultiValueMap<String, String>> kakaoRequest = new HttpEntity<>(params, headers);

// HTTP ์š”์ฒญ - POST๋ฐฉ์‹ - response ์‘๋‹ต ๋ฐ›๊ธฐ
ResponseEntity<String> response = rt.exchange(
    "https://kauth.kakao.com/oauth/token",
    HttpMethod.POST,
    kakaoRequest,
    String.class
);

์š”์ฒญ์„ ์‰ฝ๊ฒŒ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ, ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ RestTemplate ์ž…๋‹ˆ๋‹ค.

HttpHeaders๋ฅผ ์ƒ์„ฑํ•ด์„œ ํ—ค๋”์— ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ณ , MultiValueMap์„ ์ด์šฉํ•ด์„œ key:value ๋ฐ์ดํ„ฐ ํ˜•์‹์œผ๋กœ ๊ฐ’์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , HttpEntity์— ๋ฐ”๋”” ๋ถ€๋ถ„์— ํ•ด๋‹น๋˜๋Š” MultiValueMap์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. (์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ, HttpEntity๋ฅผ ๋„ฃ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•œ๋ฒˆ ๋” ๊ฐ์‹ผ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.)  ์ด์ œ kakaoRequest๋Š” ๋ฐ”๋””, ํ—ค๋”๋ฅผ ๋‘˜ ๋‹ค ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

Response:

์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ์‘๋‹ต์ด ์™”์œผ๋ฉด, ์ด์ œ ๊ณ„์ • ์ •๋ณด์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ access_token์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค!

 

์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

์š”์ฒญ์€ ์œ„์™€ ๊ฐ™์ด ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

Request:

// ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ, Access Token์„ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์‘๋‹ต ๋ฐ›๋Š” ์ฝ”๋“œ์ด๋‹ค.
HttpHeaders headers1 = new HttpHeaders();
headers1.add("Authorization", "Bearer " + kakaoOauthParams.getAccess_token());
headers1.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

HttpEntity<HttpHeaders> kakaoRequest1 = new HttpEntity<>(headers1);

ResponseEntity<String> profileResponse = rt.exchange(
	"https://kapi.kakao.com/v2/user/me",
	HttpMethod.POST,
	kakaoRequest1,
	String.class
);

kakaoOauthParams.getAccess_token() ์ด ๋ถ€๋ถ„์€ ์œ„์—์„œ ๋ฐ›์€ ์‘๋‹ต์„ ObjectMaper๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ์ฒด์— ๋‹ด์•„์„œ ๊ฐ’์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์„ฑ๊ณตํ•˜์‹œ๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~


REFERENCE

 

Kakao Developers

์นด์นด์˜ค API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•ด๋ณด์„ธ์š”. ์นด์นด์˜ค ๋กœ๊ทธ์ธ, ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ, ์นœ๊ตฌ API, ์ธ๊ณต์ง€๋Šฅ API ๋“ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

developers.kakao.com

๋ฐ˜์‘ํ˜•