시작이 반

[Spring] 2. Oauth 2.0 카카오 로그인 구현 본문

Programming/Spring

[Spring] 2. Oauth 2.0 카카오 로그인 구현

G_Gi 2021. 2. 17. 19:33
SMALL

 

 

로그인이 정상적으로 됐으면 해당 Redirect URI로 code가 넘어옴

 

    @GetMapping("/auth/kakao/callback")
    public String kakaoCallback(@RequestParam("code") String code){

        OAuthToken oAuthToken = kaKaoApiService.tokenRequest(code); //1.토큰 가져오기

        KakaoProfile kakaoProfile = kaKaoApiService.userInfoRequest(oAuthToken); //2.유저정보 가져오기
 

        int idx = kakaoProfile.getKakao_account().getEmail().indexOf("@");
        String username = kakaoProfile.getKakao_account().getEmail().substring(0, idx);
        System.out.println("유저네임:" + username);

        User kakaoUser = User.builder()
                .username(username)
                .password(kakaoProfile.getKakao_account().getEmail() + kakaoProfile.getId())
                .oauth("kakao")
                .build();

        //3.회원가입, 로그인
        userService.join(kakaoUser);

        return "redirect:/";
    }

넘어온 코드를 사용하여 Token을 만들어야함

 

 

1. 토큰 가져오기

RestTmeplate을 사용하여 요청한다.

    public OAuthToken tokenRequest(String code) {

        //POST방식으로 데이터 요청

        RestTemplate restTemplate = new RestTemplate();

        //HttpHeader
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        //HttpBody
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", clientId); //clientId 는 프로퍼티에 정의해놨음
        body.add("redirect_uri", "http://localhost:8080/auth/kakao/callback");
        body.add("code", code);

        //HttpHeader와 HttpBody 담기기
        HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(body, headers); // params : body

        return restTemplate.exchange("https://kauth.kakao.com/oauth/token", HttpMethod.POST, kakaoTokenRequest, OAuthToken.class).getBody();
    }
package Social.Sociallogin.dto;

import lombok.Data;

@Data
public class OAuthToken {
    private String access_token;
    private String token_type;
    private String refresh_token;
    private int expires_in;
    private String scope;
    private int refresh_token_expires_in;
}

 

2. 유저정보 가져오기

토큰정보를 받아오면 해당 토큰을 사용하여 유저 정보를 가져올 수 있다.

    public KakaoProfile userInfoRequest(OAuthToken oAuthToken) {

        ///유저정보 요청
        RestTemplate restTemplate = new RestTemplate();

        //HttpHeader
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + oAuthToken.getAccess_token());
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        //HttpHeader와 HttpBody 담기기
        HttpEntity<MultiValueMap<String, String>> kakaoProfileRequest = new HttpEntity<>(headers);

        return restTemplate.exchange("https://kapi.kakao.com/v2/user/me", HttpMethod.POST, kakaoProfileRequest, KakaoProfile.class).getBody();
    }
package Social.Sociallogin.dto;

import lombok.Data;

@Data
public class KakaoProfile {
    public Integer id;
    public String connected_at;
    public Properties properties;
    public KakaoAccount kakao_account;

    @Data
    public class Properties {
        public String nickname;
        public String profile_image;
        public String thumbnail_image;
    }

    @Data
    public class KakaoAccount {
        public Boolean profile_needs_agreement;
        public Profile profile;
        public Boolean has_email;
        public Boolean email_needs_agreement;
        public Boolean is_email_valid;
        public Boolean is_email_verified;
        public String email;

        @Data
        public class Profile {
            public String nickname;
            public String thumbnail_image_url;
            public String profile_image_url;
        }
    }
}

 

 

 

3. 회원가입, 로그인

유저정보를 가져왔으면 DB에 정보가 없으면 회원가입을 시킨다.

회원가입의 아이디는 주소의 @이를 기준으로 잘랐으며

비밀번호는 이메일 +  고유id를 사용하여 만들었다..

이후에 Authentication으로 등록을 해준다.

 

        int idx = kakaoProfile.getKakao_account().getEmail().indexOf("@");
        String username = kakaoProfile.getKakao_account().getEmail().substring(0, idx);

        User kakaoUser = User.builder()
                .username(username)
                .password(kakaoProfile.getKakao_account().getEmail() + kakaoProfile.getId())
                .oauth("kakao")
                .build();

        //회원가입, 로그인
        userService.join(kakaoUser);
package Social.Sociallogin.service;

import Social.Sociallogin.domain.User;
import Social.Sociallogin.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
public class UserService {

    private UserRepository userRepository;
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    private AuthenticationManager authenticationManager;

    @Autowired
    public UserService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder, AuthenticationManager authenticationManager) {
        this.userRepository = userRepository;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.authenticationManager = authenticationManager;
    }

    @Transactional
    public void  join(User user){
        if(user.getOauth() != null){//카카오 로그인
            String rawPassword = user.getPassword();
            if(!kakaovalidateDuplicateUser(user).isPresent()){
                //회원가입
                String encPassword = bCryptPasswordEncoder.encode(user.getPassword());
                user.setPassword(encPassword);
                userRepository.save(user);
            }
            Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), rawPassword));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }else{
            validateDuplicateUser(user);
            String encPassword = bCryptPasswordEncoder.encode(user.getPassword());
            user.setPassword(encPassword);
            userRepository.save(user);
        }
    }

    @Transactional(readOnly = true)
    public void validateDuplicateUser(User user){
        userRepository.findByUsername(user.getUsername())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원");
                });
    }

    @Transactional(readOnly = true)
    public Optional<User> kakaovalidateDuplicateUser(User user){
        return userRepository.findByUsername(user.getUsername());
    }




}

 

LIST