์บ ํ๊ธฐ๊ฐ ๋์ ์์
๋ก๊ทธ์ธ๋ง ๋ช ๋ฒ์งธ ๊ตฌํํ๋ ๊ฑด์ง..
ํ ๋๋ง๋ค ๋ค์ํ ๋ฐฉ์์ด ์์ด์ ๋ญ๊ฐ Best practice์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค.
์ผ๋จ ์ค์ํ๊ฑด ์กฐ๊ธ ๋ถ์กฑํ๋๋ผ๋ ๊ตฌํํ ์ค ์๋ ๊ฒ !
์ด๋ฒ์ Springboot์์ ์์ ๋ก๊ทธ์ธ์ ํด๋ณด์.
์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก (์_๋ต)
ํ๋ก ํธ ์ชฝ์์ ๋ก๊ทธ์ธ ์์ฒญ ๋ฒํผ์ด ๋๋ ธ์ ๋ Rest API ํค์ ์ฝ๋ฐฑ์ฃผ์๋ฅผ ํฌํจํ ์นด์นด์ค ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํ๋๋ก ํ๋ค.
GET /oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code HTTP/1.1
Host: kauth.kakao.com
์นด์นด์ค์์ ์์ฒญํ๋ผ๋ ๋๋ก ๊ทธ๋๋ก ๋ณด๋ด์ค๋ค.
<button id="login-kakao-btn"
onclick="location.href='https://kauth.kakao.com/oauth/authorize?client_id=[REST_APIํค]&redirect_uri=http://localhost:8080/user/kakao/callback&response_type=code'">
์ฌ์ฉ์๋ ์ด๋๋ ์นด์นด์ค ์ธ์ฆ ํ์ด์ง์์ ์ธ์ฆ์ ์ํํ ๊ฒ์ด๋ค.
์ธ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด ์ฐ๋ฆฌ๊ฐ ์ง์ ํ Redirect URI๋ก ์ธ๊ฐ์ฝ๋๋ฅผ ํฌํจํด์ ๋์์ฌ ๊ฒ์ด๋ค.
์ธ๊ฐ์ฝ๋๋ฅผ ๋ฐ์์ ์์ธ์ค ํ ํฐ์ ์์ฒญํด์ผ ํ๋ฏ๋ก Redirect URI๋ก ์ค๋ ์์ฒญ์ ๋ฐ์ ์ ์๋ ์ปจํธ๋กค๋ฌ ๋ฉ์๋๋ฅผ ์์ฑํ๋ค.
@GetMapping("/user/kakao/callback")
public String kakaoLogin(String code) {
userService.kakaoLogin(code);
return "redirect:/";
}

์ URL๋ก Required๊ฐ O์ธ (ํ์) ํ๋ผ๋ฏธํฐ๋ง ๊ตฌ์ฑํด์ ๋ณด๋ด์ฃผ๋ฉด ๋๋ค.
public void kakaoLogin(String authorizedCode) {
String accessToken = getAccessToken(authorizedCode);
}
HTTP ์์ฒญ์ ๋ณด๋ด๋ ค๋ฉด ํฌ๊ฒ ํค๋์ ๋ฐ๋๋ฅผ ํ์๋ก ํ๋ค.
HttpHeadersMultiValueMapํค๋์ ๋ฐ๋๋ฅผ ๊ตฌ์ฑํ๋ค๋ฉด HttpEntity๋ก ํค๋์ ๋ฐ๋๋ฅผ ๋ฌถ์ด์ค ์ ์๋ค.
HttpEntity์ ์ ๋ค๋ฆญ์ ๋ฐ๋์ ํ์
์ด๋ค.
Http ์์ฒญ์ RestTemplate์ ์ฌ์ฉํ๋ค.
private String getAccessToken(String authorizedCode) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
paramMap.add("grant_type", "authorization_code");
paramMap.add("client_id", "๋ด๊บผAPI_KEY");
paramMap.add("redirect_uri", "http://localhost:8080/user/kakao/callback");
paramMap.add("code", authorizedCode);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(paramMap, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
requestEntity,
String.class
);
String body = response.getBody();
JSONObject tokenJson = new JSONObject(body);
return tokenJson.getString("access_token");
}
์๋ต์ ์๋์ ๊ฐ์ด ์จ๋ค.
ํ์ํ๊ฑด ์์ธ์ค ํ ํฐ์ด๋๊น access_token ๋ถ๋ถ๋ง ํ์ฑํ๋ค.


์์ฒญ์ ์์ ๊ฐ์ด ๊ตฌ์ฑํ๋ค.
๋์ผํ๊ฒ ํค๋์ ๋ฐ๋๋ฅผ ๊ตฌ์ฑํด์ HttpEntity๋ก ๋ฌถ๊ณ RestTemplate์ผ๋ก ์์ฒญํ๋ฉด ๋๋ค.
(ํ์ ์์ฒญ ํ๋ผ๋ฏธํฐ๋ ์์ผ๋ฏ๋ก ๋ฐ๋๋ ๋น์๋๋ค.)
private KakaoUserInfo getKakaoUserInfo(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoProfileRequest,
String.class
);
JSONObject body = new JSONObject(response.getBody());
Long id = body.getLong("id");
String email = body.getJSONObject("kakao_account").getString("email");
String nickname = body.getJSONObject("properties").getString("nickname");
return new KakaoUserInfo(id, email, nickname);
}
์๋ต์ํ

ํ์ํ ๊ฒ๋ง ์ ๋ฝ์์ User DB๋ฅผ ์ ์ ํ๊ฒ ๊ตฌ์ฑํ๋ฉด ๋๋ค.
ํ์๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ์ํ user DB๋ฅผ ๊ตฌ์ฑํ๋ ๋ฐฉ์์ ๊ทธ๋ ๊ทธ๋ ๋ง์ถฐ์ ํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
์ค์ํ ๊ฑด ๋ก๊ทธ์ธ ์ฒ๋ฆฌ์ด๋ค.
Spring Security๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ Spring Security์ ๋ฃฐ์ ๋ฐ๋ผ์ผ ํ๋ค.
์ง๊ธ ์นด์นด์ค๋ฅผ ํตํด ์ธ์ฆ์ ๋์ง๋ง Spring Security๋ ์ด ์ฌ์ค์ ๋ชจ๋ฅธ๋ค.
์๋ ค์ค์ผ ์ด ์ธ์ฆ์ด ์ธ์
๊ธฐ๋ฐ์ผ๋ก ์ญ ์ด์ด์ง ์ ์๋ค.
์์๋ก ์ธ์ฆ์ ํ๊ธฐ ์ํด์๋ AuthenticationManager๊ฐ ํ์ํ๋ค.
AuthenticationManager๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด ์ผ๋จ ๋น์ผ๋ก ๋ฑ๋กํ๋ค.
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
์ด์ ์์๋๋ก ์ด๋ค ํํฐ๋ฅผ ์ง๋ ํ ํฐ์ ๋ฐํ๋ฐ์ ๊ฑธ๋ก ๊ฐ์ ํ๊ณ ํ ํฐ์ ์์ฑํ๋ค.
์ ๋ต์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ง๋ง ์ด๋ฒ ์ค์ต์์๋ username๊ณผ password๋ฅผ ์์๋ก ์์ฑํ์ผ๋ฏ๋ก UsernamePasswordAuthenticationToken ์ ํตํด ํ ํฐ์ ์์ฑํ๋ค.
์ด ํ ํฐ์ด ์ผ์ข
์ ํตํ์ฆ์ด ๋๋๊ฑด๋ฐ UsernamePasswordAuthenticationToken์ ํตํด ์์ฑ๋ ํ ํฐ์ ์์ง ์ธ์ฆ๋์ง ์์๋ค๋ ์๋ฏธ๋ก isAuthenticated ํ๋๊ฐ false ์ด๋ค.

AuthenticationManager๋ฅผ ์ด์ฉํด์ ์์๋ก ์ธ์ฆ๋ ํ ํฐ์ด๋ผ๊ณ ์ฒดํฌํ๋ค.
Authentication authentication = authenticationManager.authenticate(kakaoUsernamePassword);
์ด์ ํ ํฐ์ด ์ธ์ฆ๋์์ผ๋ฏ๋ก authentication ์ ์ธํ
ํ๋ค.
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder > SecurityContext > Authentication
์์ง ์์ธํ๋ ํ์
๋์ง ์์ง๋ง SecurityContext์ Authentication์ ํ ํฐ์ ์ธํ
ํ๋ฉด ์ดํ ์ปจํธ๋กค๋ฌ์์ @AuthenticationPrincipal๋ก ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
์ด๊ฒ๋ง ์์๋ ์ถฉ๋ถํ์ง ์์๊น...? ใ