저번시간에 이어서 카카오가 준 Token을 이용해서 회원가입과 로그인을 구현해보자.
@Column(length = 100) // provider 추가 (kakao)
private String provider;
다음으론 사용자 정보를 가져오기 위해 토큰값으로 사용자의 정보를 카카오 서버에 요청한다. -> getKakaoProfile
public KakaoProfile getKakaoProfile(String kakaoAccessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Authorization", "Bearer " + kakaoAccessToken);
String requestUrl = env.getProperty("social.kakao.url.profile");
if (requestUrl == null) throw new AccountsExceptionHandler(ErrorCode._INTERNAL_SERVER_ERROR);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(null, headers);
try {
ResponseEntity<String> response = restTemplate.postForEntity(requestUrl, request, String.class);
if (response.getStatusCode() == HttpStatus.OK)
return gson.fromJson(response.getBody(), KakaoProfile.class);
} catch (Exception e) {
log.error(e.toString());
throw new AccountsExceptionHandler(ErrorCode._INTERNAL_SERVER_ERROR);
}
throw new AccountsExceptionHandler(ErrorCode._INTERNAL_SERVER_ERROR);
}
카카오 프로필 매핑 Dto
@Getter
public class KakaoProfile {
private Long id;
private Properties properties;
private KakaoAccount kakao_account;
@Getter
@ToString
public static class KakaoAccount {
private String email;
}
@Getter
@ToString
public static class Properties {
private String nickname;
}
}
위에서 구현한 사용자 정보를 통해 KakaoProfile 객체를 만들 수 있다. 이 객체에는 사용자 정보가 들어있으므로 해당 정보로 회원가입을 한다.
@PostMapping("/social/signup/kakao")
public ApiResponse<UserSignupResponseDto> signupByKakao(@Valid @RequestBody UserSocialSignupRequestDto requestDto) {
return ApiResponse.onSuccess(kakaoService.signupByKakao(requestDto));
}
public UserSignupResponseDto signupByKakao(UserSocialSignupRequestDto requestDto) {
KakaoProfile kakaoProfile = getKakaoProfile(requestDto.accessToken());
if (kakaoProfile == null) throw new AccountsExceptionHandler(ErrorCode.USER_NOT_FOUND);
// if (kakaoProfile.getKakao_account().getEmail() == null) {
// kakaoUnlink(requestDto.accessToken());
// throw new AccountsExceptionHandler(ErrorCode.EMAIL_NOT_EXIST);
// }
UserSignupRequestDto signupRequestDto = UserSignupRequestDto.builder()
.email(requestDto.email())
.name(kakaoProfile.getProperties().getNickname())
.nickName(kakaoProfile.getProperties().getNickname())
.provider("kakao")
.build();
return accountsService.socialSignup(signupRequestDto);
}
public UserSignupResponseDto socialSignup(UserSignupRequestDto userSignupRequestDto) {
if (userJpaRepository
.findByEmailAndProvider(userSignupRequestDto.email(), userSignupRequestDto.provider())
.isPresent()
) throw new AccountsExceptionHandler(ErrorCode.USER_ALREADY_EXIST);
User user = userJpaRepository.save(userSignupRequestDto.toEntity());
return UserSignupResponseDto.from(user);
}
원래는 카카오 프로필을 통해 이메일을 가져와야하지만 현재 이메일을 가져올 수 있는 권한이 없다.(예전엔 된거 같은데 지금은 비즈앱 전환? 이라는 걸 해야하나보다.) 그래서 임시로 입력받아 가져오게 하였다.
회원가입 후에 카카오에서 사용자 정보 가져오기를 요청해서 로그인을 하도록 구현한다. 회원이 존재하면 JWT를 발급시켜준다. 이제 유저는 Authentication된 유저가 되고 서비스의 AccessToken을 발급받아서 서비스를 요청할 수 있게된다.
@PostMapping("/social/login/kakao")
public ApiResponse<UserLoginResponseDto> loginByKakao(@Valid @RequestBody UserSocialLoginRequestDto requestDto) {
return ApiResponse.onSuccess(kakaoService.loginByKakao(requestDto));
}
public UserLoginResponseDto loginByKakao(UserSocialLoginRequestDto requestDto) {
KakaoProfile kakaoProfile = getKakaoProfile(requestDto.accessToken());
if (kakaoProfile == null) throw new AccountsExceptionHandler(ErrorCode.USER_NOT_FOUND);
// String kakaoEmail = kakaoProfile.getKakao_account().getEmail();
// if (kakaoEmail == null) throw new AccountsExceptionHandler(ErrorCode.EMAIL_NOT_EXIST);
User user = userJpaRepository.findByEmailAndProvider(requestDto.email(), "kakao")
.orElseThrow(() -> new AccountsExceptionHandler(ErrorCode.USER_NOT_FOUND));
CustomUserDetails customUserDetails = new CustomUserDetails(user);
String accessToken = jwtProvider.createJwtAccessToken(customUserDetails);
String refreshToken = jwtProvider.createJwtRefreshToken(customUserDetails);
return UserLoginResponseDto.builder()
.userId(user.getId())
.createdAt(LocalDateTime.now())
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
로그인도 회원가입가 마찬가지로 임시로 이메일을 처리했다.