Spring boot 프로젝트 accounts(3) - OAuth 2.0 와 카카오 로그인 2

·2024년 3월 21일
0

저번시간에 이어서 카카오가 준 Token을 이용해서 회원가입과 로그인을 구현해보자.

  • AccessToken으로 로그인할 경우
    • AccessToken으로 카카오 API 서버의 profile api에 사용자 정보 요청
    • 해당 카카오 정보를 통해 서비스에 가입되어있는지 확인
      • 가입되어 있지 않다면 로그인 실패
      • 가입되어 있다면 JWT 발급
  • AccessToken으로 가입할 경우
    • AccessToken으로 카카오 API 서버에 profile api를 통해 사용자 정보 요청
    • 해당 카카오 정보를 통해 서비스에 가입되어있는지 확인
      - 가입되어 있지 않았다면 새로 가입시키고 JWT 발급
      - 이미 가입 되어있다면 가입 실패
      먼저 User Entity에 소셜 가입이 가능하도록 provider 값을 추가하자.

      @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();
    }

로그인도 회원가입가 마찬가지로 임시로 이메일을 처리했다.

profile
고민0

0개의 댓글