์ด๋ฒ์ฃผ๋ ํด๋ก ์ฝ๋ฉ ์ฃผ์ฐจ๋ก ์ฐ๋ฆฌํ์ ์ํฐ๋์ ์ปค๋ฎค๋ํฐ๋ฅผ ํด๋ก ์ฝ๋ฉ ํ๊ธฐ๋กํ์๋ค โจ๏ธ
๋ด๊ฐ ๋งก์ ๊ธฐ๋ฅ์ ์นด์นด์ค ๋ก๊ทธ์ธ ๐ ์์์ ์ฌ์ ์ผ๋ ํ ์คํธ๋ถํฐ ์ฝ์ง ์์์ ธ์ ์ข์ ํ๊ณ ์๋ ์์ค์
์ฒ์ฌ ์์๋์ ๋๋ถ์ผ๋ก ๋ฌด์๋ฌด์ํ ์๋ฌ์์ ๋ฒ์ด๋๊ณ .. ๋ค์๋ ๋๊ฐ์ ์ผ์ ๊ฒช์ง์ํด ํ๋ ํฌ์คํ .. ์์ง๋ง .. ! ๊ธฐ์ตํด ๋ด ..!
๐๐ป ๋์์ค ์นด์นด์ค ๋ก๊ทธ์ธ !
์ฐ์ ๋๋ ํ๋ก ํธ์ ์ฐ๊ฒฐ ์ , ๋์ ๋ณด์ด๋ ์นด์นด์ค ๋ก๊ทธ์ธ ๋ฒํผ์ด ์๋ ์ํ์์์ ์นด์นด์ค ๋ก๊ทธ์ธ ์๋ ๋ฐฉ์์ ์ ์ด๋๋ ค๊ณ ํ๋ค.
๋ฒํผ์ด ์์ผ๋ฉด ๋ณ๋ก ์ด๋ ต์ง์์ !.. ์๋ง๋ ,,
์ฐ์ ์ฌ์ฉํ ์์ค์ฝ๋๋ ์๋์ ๊ฐ๋ค.
1 | User Controller
@PostMapping("/loginKakao")
public MsgResponseDto kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
// code: ์นด์นด์ค ์๋ฒ๋ก๋ถํฐ ๋ฐ์ ์ธ๊ฐ ์ฝ๋
String createToken = userKakaoService.kakaoLogin(code, response);
// Cookie ์์ฑ ๋ฐ ์ง์ ๋ธ๋ผ์ฐ์ ์ Set
Cookie cookie = new Cookie(JwtUtil.AUTHORIZATION_HEADER, createToken.substring(7));
cookie.setPath("/");
response.addCookie(cookie);
return new MsgResponseDto(SuccessCode.LOG_IN);
}
2| User Service
@Service
@RequiredArgsConstructor
public class UserKakaoService {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private static int signUpType = 1; // ์ผ๋ฐ ๋ก๊ทธ์ธ User, ์นด์นด์ค ๋ก๊ทธ์ธ User ์๋ณ์ฉ
public String kakaoLogin(String code, HttpServletResponse response) throws JsonProcessingException {
// 1. "์ธ๊ฐ ์ฝ๋"๋ก "์ก์ธ์ค ํ ํฐ" ์์ฒญ
String accessToken = getToken(code);
// 2. ํ ํฐ์ผ๋ก ์นด์นด์ค API ํธ์ถ : "์ก์ธ์ค ํ ํฐ"์ผ๋ก "์นด์นด์ค ์ฌ์ฉ์ ์ ๋ณด" ๊ฐ์ ธ์ค๊ธฐ
LoginKakaoRequestDto kakaoUserInfo = getKakaoUserInfo(accessToken);
// LoginKakaoRequestDto kakaoUserInfo = getKakaoUserInfo(code);
// 3. ํ์์์ ํ์๊ฐ์
User kakaoUser = registerKakaoUser(kakaoUserInfo);
// 4. JWT ํ ํฐ ๋ฐํ
String createToken = jwtUtil.createToken(kakaoUser.getUserId(), kakaoUser.getRole());
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, createToken);
return createToken;
}
// 1. "์ธ๊ฐ ์ฝ๋"๋ก "์ก์ธ์ค ํ ํฐ" ์์ฒญ
private String getToken(String code) throws JsonProcessingException {
// HTTP Header ์์ฑ
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body ์์ฑ
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "๋ดํ ํฐ");
body.add("redirect_uri", "http://localhost:8080/api/user/kakao/callback");
body.add("code", code);
// HTTP ์์ฒญ ๋ณด๋ด๊ธฐ
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// HTTP ์๋ต (JSON) -> ์ก์ธ์ค ํ ํฐ ํ์ฑ
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();
}
// 2. ํ ํฐ์ผ๋ก ์นด์นด์ค API ํธ์ถ : "์ก์ธ์ค ํ ํฐ"์ผ๋ก "์นด์นด์ค ์ฌ์ฉ์ ์ ๋ณด" ๊ฐ์ ธ์ค๊ธฐ
private LoginKakaoRequestDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
String nickname = RandomStringUtils.random(15, true, true); // ๋๋ค์ ๋๋ค ์์ฑ
// HTTP Header ์์ฑ
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP ์์ฒญ ๋ณด๋ด๊ธฐ
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
String email = jsonNode.get("kakao_account")
.get("email").asText();
return new LoginKakaoRequestDto(email, nickname);
}
// 3. ํ์์์ ํ์๊ฐ์
private User registerKakaoUser(LoginKakaoRequestDto kakaoUserInfo) {
String nickname = RandomStringUtils.random(15, true, true);
String kakaoId = kakaoUserInfo.getUserId(); // DB ์ ์ค๋ณต๋ Kakao Id ๊ฐ ์๋์ง ํ์ธ
User kakaoUser = userRepository.findByUserId(kakaoId)
.orElse(null);
if (kakaoUser != null) { // ์นด์นด์ค ์ฌ์ฉ์ email ๋์ผํ email ๊ฐ์ง ํ์์ด ์๋์ง ํ์ธ
User sameEmailUser = userRepository.findByUserId(kakaoUser.getUserId()).orElse(null);
sameEmailUser.update(signUpType);
} else{
// ์ ๊ท ํ์๊ฐ์
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getUserId();
kakaoUser = new User(kakaoId, encodedPassword, nickname, signUpType, UserRoleEnum.USER);
}
userRepository.save(kakaoUser);
return kakaoUser;
}
}
3| OAuth2 ์นด์นด์ค ๋ก๊ทธ์ธ ์ฐ๋ ๋ก์ง
1. Redirect_Uri
: ์นด์นด์ค ๋ก๊ทธ์ธ ํ๋ฉด์ ํธ์ถํ๊ณ , ์ฌ์ฉ์ ๋์๋ฅผ ๊ฑฐ์ณ์ ์ธ๊ฐ ์ฝ๋ ๋ฐ๊ธ์ ์์ฒญ
# ์ธ๊ฐ์ฝ๋ ์์ฒญ Url
https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri=http://localhost:8080/api/user/loginKakao&response_type=code
๐ Rest_API_KEY: ๋๋ฒจ๋กํผ์ค์์ ๋ด REST APIํค ์ฝ์
# ํ๋ก ํธ๊ฐ ์์ ๊ฒฝ์ฐlogin.html์ ์๋ ๋ด์ฉ์ ๋ฃ์ด์ฃผ๋ฉด ๋จ
https://kauth.kakao.com/oauth/authorize?client_id=๋ณธ์ธ์ REST APIํค&redirect_uri=http://localhost:8080/api/user/loginKakao&response_type=code
๐ ์ฌ์ฉ์๊ฐ [๋์ํ๊ณ ๊ณ์ํ๊ธฐ] ์ ํ ํ ๋ก๊ทธ์ธ ์์ฒญ์ด ์น์ธ๋๋ฉด ํ ํฐ์ ๋ฐ๊ธฐ ์ํ ์ธ๊ฐ ์ฝ๋๋ฅผ ์ ์กํด์ค
2. ํ ํฐ ๋ฐ๊ธฐ
: ์ธ๊ฐ ์ฝ๋๋ฅผ ๋ฐ์ ๋ค, ์ธ๊ฐ ์ฝ๋๋ก ์ก์ธ์ค ํ ํฐ๊ณผ ๋ฆฌํ๋ ์ ํ ํฐ์ ๋ฐ๊ธ๋ฐ๋ API
๐ ์ธ๊ฐ์ฝ๋ + ํ ํฐ์ ํตํด ์นด์นด์ค ๋ก๊ทธ์ธ์ ์๋ํ ์ ์์ด์ ํ ํฐ ๋ฐ๊ธ์ ์์ฒญํ๋ค.
3. ์ฌ์ฉ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
๐ ์์์ ์์ฑํ ํ ํฐ์ ๊ฐ์ง๊ณ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ฝ์ด์์ ๋ก๊ทธ์ธ ํ๊ฑฐ๋, ํ์๊ฐ์ ํ๋ ๋ก์ง์ ๋ง๋ ๋ค.
4| POSTMAN์ ์ด์ฉํ ์นด์นด์ค ๋ก๊ทธ์ธ ํ ์คํธํ๊ธฐ
1. ์ธ๊ฐ์ฝ๋ ๋ฐ๊ธํ๊ธฐ
๐ ํฌ์คํธ๋งจ > Authorization > Type:OAuth2.0 > Configure New Token์ ๋ด๊ฐ ํ์ํ ๋ด์ฉ๋ค ๋ฃ๊ธฐ > Get New Access Token ์์ฑ์ ๋๋ฅด๋ฉด
๐ ์๋ ์ฝ๋กฌ Complete ๋ฉ์ธ์ง๊ฐ ๋จ๊ตฌ
๐ ์ฌ๊ธฐ์ Access Token์ ๋ณต์ฌํ๊ธ
๋๋ ์ฌ๊ธฐ์ ๋ฌด์ง๋ฌด์ง๋ฌด์ง ์ค๋๊ฑธ๋ ธ๋๋ฐ Client Authentication์ Body๋ก ๋ฐ๊พธ์ง ์์๊ธฐ ๋๋ฌธ .. ์์๋ ๊ฐ์ฌํฉ๋๋น .. ๐ฅบ
2. ๋ก๊ทธ์ธ ์๋ / ํ ํฐ ์์ฑ
๐ Request Params ํ์์ผ๋ก ์์์ ๋ณต์ฌํ Access Token์ Code์ ๋ฃ์ด์ ๋ณด๋ด์ฃผ๋ฉด ๋ก๊ทธ์ธ์ด ๋๋ต
๐ ๋์ ๊ฒฝ์ฐ๋ ์ ์์ด๋์ ๋์ผํ ํ์์ด ์์๊ฒฝ์ฐ SignUpType์ ์นด์นด์ค ๋ก๊ทธ์ธ์ผ๋ก ๋ณ๊ฒฝํ๋ค.
๐ ๊ทธ๋ผ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ค๋ ๋ฉ์ธ์ง๊ฐ ๋จ๊ณ , ์ํ๋ Jwt Token์ ์ป์ ์ ์๋ค!
3) POSTMAN์ผ๋ก ๋ก๊ทธ์ธ ํ ์คํธ ์์ฒญ ์ ์ฝ๋ ๋ณ๊ฒฝ
๐ ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ๋ฐ๊ฟ์ ํ ์คํธ ํด์ผํจ! ํฌ์คํธ๋งจ์ผ๋ก ํ๋ฉด ์ธ๊ฐ์ฝ๋๋ก ์ก์ธ์ค ํ ํฐ์ ์์ฒญํ๋ ๋ถ๋ถ์ด ํจ์ค ๋๋ ๊ฒ ๊ฐ์ (?)
'๐ฝ Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[๊ธฐ์ ๋ฉด์ ์คํฐ๋] Filter / Interceptor / AOP ์ ์์ ์ฐจ์ด์ (0) | 2023.02.23 |
---|---|
[๊ธฐ์ ๋ฉด์ ์คํฐ๋] DI ํ๋ vs ์์ฑ์ ์ฃผ์ ๋ฐฉ์ (0) | 2023.02.23 |
Spring | @Transactional์ด ๋ญ๋ฐ (0) | 2022.12.15 |
Spring | AOP๊ฐ ๋ญ๋ฐ (0) | 2022.12.15 |
Spring | DI, IOC๊ฐ ๋ญ๋ฐ (0) | 2022.12.08 |