์บ ํ๊ธฐ๊ฐ ๋์ ์์
๋ก๊ทธ์ธ๋ง ๋ช ๋ฒ์งธ ๊ตฌํํ๋ ๊ฑด์ง..
ํ ๋๋ง๋ค ๋ค์ํ ๋ฐฉ์์ด ์์ด์ ๋ญ๊ฐ 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 ์์ฒญ์ ๋ณด๋ด๋ ค๋ฉด ํฌ๊ฒ ํค๋์ ๋ฐ๋๋ฅผ ํ์๋ก ํ๋ค.
HttpHeaders
MultiValueMap
ํค๋์ ๋ฐ๋๋ฅผ ๊ตฌ์ฑํ๋ค๋ฉด 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
๋ก ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
์ด๊ฒ๋ง ์์๋ ์ถฉ๋ถํ์ง ์์๊น...? ใ