Dev-Kyuu
article thumbnail

์ด๋ฒˆ์ฃผ๋Š” ํด๋ก ์ฝ”๋”ฉ ์ฃผ์ฐจ๋กœ ์šฐ๋ฆฌํŒ€์€ ์›ํ‹ฐ๋“œ์˜ ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ํด๋ก ์ฝ”๋”ฉ ํ•˜๊ธฐ๋กœํ•˜์˜€๋‹ค โŒจ๏ธ

๋‚ด๊ฐ€ ๋งก์€ ๊ธฐ๋Šฅ์€ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๐Ÿ˜Ž ์‹œ์ž‘์€ ์‰ฌ์› ์œผ๋‚˜ ํ…Œ์ŠคํŠธ๋ถ€ํ„ฐ ์‰ฝ์ง€ ์•Š์•„์ ธ์„œ ์ขŒ์ ˆํ•˜๊ณ  ์žˆ๋Š” ์™€์ค‘์—

์ฒœ์‚ฌ ์˜์˜๋‹˜์˜ ๋•๋ถ„์œผ๋กœ ๋ฌด์‹œ๋ฌด์‹œํ•œ ์—๋Ÿฌ์—์„œ ๋ฒ—์–ด๋‚˜๊ณ  .. ๋‹ค์‹œ๋Š” ๋˜‘๊ฐ™์€ ์ผ์„ ๊ฒช์ง€์œ„ํ•ด ํ•˜๋Š” ํฌ์ŠคํŒ… .. ์žŠ์ง€๋งˆ .. ! ๊ธฐ์–ตํ•ด ๋‚ด ..!

 

๐Ÿ™‹๐Ÿป ๋„์™€์ค˜ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ! 

์šฐ์„  ๋‚˜๋Š” ํ”„๋ก ํŠธ์™€ ์—ฐ๊ฒฐ ์ „, ๋ˆˆ์— ๋ณด์ด๋Š” ์นด์นด์˜ค ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์ด ์—†๋Š” ์ƒํƒœ์—์„œ์˜ ์นด์นด์˜ค ๋กœ๊ทธ์ธ ์‹œ๋„ ๋ฐฉ์‹์„ ์ ์–ด๋‘๋ ค๊ณ ํ•œ๋‹ค.

๋ฒ„ํŠผ์ด ์žˆ์œผ๋ฉด ๋ณ„๋กœ ์–ด๋ ต์ง€์•Š์›€ !.. ์•„๋งˆ๋‘ ,,

 

์šฐ์„  ์‚ฌ์šฉํ•œ ์†Œ์Šค์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

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์œผ๋กœ ๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ ์š”์ฒญ ์‹œ ์ฝ”๋“œ ๋ณ€๊ฒฝ

๐Ÿ‘‰ ์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๋ฐ”๊ฟ”์„œ ํ…Œ์ŠคํŠธ ํ•ด์•ผํ•จ! ํฌ์ŠคํŠธ๋งจ์œผ๋กœ ํ•˜๋ฉด ์ธ๊ฐ€์ฝ”๋“œ๋กœ ์•ก์„ธ์Šค ํ† ํฐ์„ ์š”์ฒญํ•˜๋Š” ๋ถ€๋ถ„์ด ํŒจ์Šค ๋˜๋Š” ๊ฒƒ ๊ฐ™์›€ (?)

profile

Dev-Kyuu

@kyuu_ng

ํฌ์ŠคํŒ…์ด ์ข‹์•˜๋‹ค๋ฉด "์ข‹์•„์š”โค๏ธ" ๋˜๋Š” "๊ตฌ๋…๐Ÿ‘๐Ÿป" ํ•ด์ฃผ์„ธ์š”!