ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [๊ตฌํ˜„] Spring์—์„œ ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ
    SPRING/PROJECT 2023. 9. 25. 01:17

    ๊ณ„ํš

    ํด๋ผ์ด์–ธํŠธ

    ์นด์นด์˜คํ†ก ๋กœ๊ทธ์ธ์„ ์‹คํ–‰ํ•˜์—ฌ ์„œ๋ฒ„์— ์นด์นด์˜คํ†ก ์„œ๋ฒ„์—์„œ ๋ฐ›์€ access_token์„ ๋„˜๊ฒจ์ค€๋‹ค.

    โฌ‡

    ์„œ๋ฒ„

    ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ›์€ access_token์œผ๋กœ ์นด์นด์˜คํ†ก ์„œ๋ฒ„์—์„œ ํ•ด๋‹น User ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜จ๋‹ค. 

    ํ•„์š”ํ•œ ์ •๋ณด์™€ ํ•จ๊ป˜ User๋ฅผ ์ƒ์„ฑํ•ด DB์— ์ €์žฅํ•œ๋‹ค.

    ์šฐ๋ฆฌ ์„œ๋น„์Šค์—์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” JWT ํ† ํฐ์„ ์ƒ์„ฑํ•œ๋‹ค.

    ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ JWT ํ† ํฐ์„ ๋„˜๊ฒจ์ค€๋‹ค.

    โฌ‡

    ํด๋ผ์ด์–ธํŠธ

    ํ—ค๋”์— JWT ํ† ํฐ์„ ๋„ฃ์–ด request ํ•จ์œผ๋กœ์จ ์„œ๋ฒ„๊ฐ€ ํ˜„์žฌ User ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ๋” ํ•œ๋‹ค.

     

    ์—‘์„ธ์Šค ํ† ํฐ ๋ฐ›์•„์˜ค๊ธฐ

    ์šฐ๋ฆฌํ•œํ… ํ…Œ์ŠคํŠธ์šฉ ์—‘์„ธ์Šค ํ† ํฐ์ด ํ•„์š”ํ•˜๋‹ค.

    ์ง€๊ธˆ ์งค ์ฝ”๋“œ๋Š” ์‹ค์ œ API๋กœ ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ๊ด€๋ฆฌ์ž์šฉ์œผ๋กœ ์‚ฌ์šฉํ•  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

    Controller์— ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

    @PostMapping("/manager/auth/kakao")//...b
    public ResponseEntity<ResponseTemplate> kakaologin(@RequestParam(name = "code") String controllerCode) { // ...c
        String accessToken = managerservice.getKakaoAccessToken(controllerCode);
        System.out.println(accessToken);
        System.out.println(managerservice.getUserInfo(accessToken));
        return ResponseTemplate.toResponseEntity(OK_SUCCESS);
    }

    ์šฐ๋ฆฌ๋Š” ์ € b url๋กœ ์ € ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด

    ์นด์นด์˜คํ†ก์—์„œ ์ฃผ๋Š” access token๊ณผ access token์œผ๋กœ ์ง์ ‘ ์นด์นด์˜ค ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜จ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ System.out.println์œผ๋กœ ๋กœ๊ทธ์ฐฝ์— ์ฐ์„ ๊ฒƒ์ด๋‹ค.

    ์–ด๋””๊นŒ์ง€๋‚˜ ์ž„์‹œ ํ…Œ์ŠคํŠธ์šฉ์ด๊ณ , ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ์ฝ”๋“œ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ System.์–ด์ฉŒ๊ตฌ๋กœ ์ฐ์—ˆ๋‹ค.

    ์‹ค์ œ๋กœ๋Š” access token๋Š” controller์—์„œ ๋ฐ›์•„์˜ค๊ณ , ์šฐ๋ฆฌ๋Š” ๊ทธ access token์œผ๋กœ getUserInfo๋งŒ ํ•  ๊ฒƒ์ด๋‹ค.

     

    ์œ„์—์„œ ์‚ฌ์šฉํ•œ ManagerService์˜ ๋ฉ”์†Œ๋“œ์ด๋‹ค.

    ์ด๋ฆ„์€ ์ƒ๊ด€ ์—†์ด Service ๋‹จ์—๋‹ค๊ฐ€ ๋‘๋ฉด ๋œ๋‹ค.

    getKakaoAccessToken ๋ฉ”์„œ๋“œ๋Š” ์—ญ์‹œ ๊ทธ์ € access token์„ ๊ฐ€์ ธ์˜ฌ ์šฉ๋„์ด๊ธฐ ๋•Œ๋ฌธ์ด๊ธฐ์— ์ž„์‹œ ๋ฉ”์„œ๋“œ์ด๋‹ค.

    getUserInfo๋Š” ๋‚˜๋Š” ๋‹ค๋ฅธ ์†Œ์…œ ๋กœ๊ทธ์ธ๋„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์„œ๋“œ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์—ˆ๊ธฐ์— ManagerService์— ๋‘์—ˆ๋Š”๋ฐ,

    ์ด๋Œ€๋กœ ์‚ฌ์šฉํ•  ์‚ฌ๋žŒ์€ ๋‹ค๋ฅธ Service์— ๋‘ฌ๋„ ์ข‹๋‹ค.

    //application.yml ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ์— ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ด๋‘์—ˆ๋‹ค.
    @Value("${oauth2.kakao.rest-api}")
    private String restAPIKey;
    
    public String getKakaoAccessToken(String controllerCode) {
        String reqURL = "https://kauth.kakao.com/oauth/token";
        String accessToken = "";
    
        try {
            //reqURL๋กœ ์„œ๋ฒ„์™€ ํ†ต์‹  ์‹œ์ž‘ http ์—ฐ๊ฒฐ
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    
            //POST๋กœ ํ†ต์‹ ํ• ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์‘๋‹ต์„ ๋ฐ›๊ฒ ๋‹ค.
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
    
            //ํ†ต์‹ ํ•  url ๋งŒ๋“ค๊ธฐ. bw๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ conn์— body๋กœ ๋ถ™์ธ๋‹ค.
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
            StringBuilder sb = new StringBuilder();
            sb.append("grant_type=authorization_code");
            sb.append("&client_id=" + restAPIKey);
            sb.append("&redirect_uri=http://localhost:8080/manager/auth/kakao");
            sb.append("&code=" + controllerCode);
            bw.write(sb.toString());
            bw.flush();
            bw.close();
    
            //๋งŒ๋“ค์–ด์ง„ conn์œผ๋กœ ์‘๋‹ต์„ ๋ฐ›์•„ Json ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ, ๊ทธ ์ค‘ access_token๊ฐ’๋งŒ ๋ฐ›์•„์˜จ๋‹ค.
            JsonElement element = JsonParser.parseString(getConnectionResponse(conn.getInputStream()));
            accessToken = element.getAsJsonObject().get("access_token").getAsString();
    
        } catch (IOException e) {
            throw new CustomException(SERVER_ERROR_CONNECTION);
        }
    
        return accessToken;
    }
    
    public String getUserInfo(String accessToken) {
    
        String reqURL = "https://kapi.kakao.com/v2/user/me";
        String response;
        try {
            //accessToken์„ ํ—ค๋”๋กœ ๋ถ™์—ฌ์„œ ํ†ต์‹ 
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Authorization", "Bearer " + accessToken);
    
            //ํšŒ์› ์ •๋ณด ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ
            JsonElement element = JsonParser.parseString(getConnectionResponse(conn.getInputStream()));
            response = element.getAsJsonObject().toString();
        } catch (IOException e) {
            throw new CustomException(FORBIDDEN_TOKEN_NOT_VALID);
        }
    
        return response;
    }
    
    //๋ฐ›์•„์˜จ inputStream String์œผ๋กœ ๋ฐ”๊พธ๋Š” ๋ฉ”์„œ๋“œ
    private String getConnectionResponse(InputStream inputStream) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        String result = "";
    
        while ((line = br.readLine()) != null) {
            result += line;
        }
    
        br.close();
    
        return result;
    }

     

    ์นด์นด์˜คํ†ก ์„œ๋ฒ„ ์—ฐ๊ฒฐ

    ์ฒ˜์Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ์„ฑํ•˜๋ฉด ํ‚ค๊ฐ€ ํ•จ๊ป˜ ์ƒ์„ฑ๋œ๋‹ค.

    ์ € ํ‚ค๋“ค ์ค‘ REST APIํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ... a

    ์นด์นด์˜ค ๋กœ๊ทธ์ธ ํ™œ์„ฑํ™” ํ•ด์ฃผ๊ณ  Redirect URI์— ์นด์นด์˜ค ๋กœ๊ทธ์ธ ํ›„ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š” API URI๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค€๋‹ค

    ์œ„์˜ ....b๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋จ

     

    ๋‹ค์Œ ์•„๋ž˜ ๋งํฌ๋กœ ์ ‘์†ํ•œ๋‹ค.

    https://kauth.kakao.com/oauth/authorize?client_id={REST_API_KEY}&redirect_uri={REDIRECT_URI}&response_type=code 

    REST_API_KEY๋Š” ์œ„์˜ a์ด๋ฉฐ REDIRECT_URI๋Š” ์œ„์˜ b์ด๋‹ค.

     

    +) ํ˜น์‹œ 

    Invalid character found in method name. HTTP method names must be tokens

    ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด,

    https ์—ฐ๊ฒฐ์€ ํ•ด๋†“์ง€ ์•Š์€ ์„œ๋ฒ„์—์„œ REDIRECT_URI๋ฅผ https๋กœ ์ž˜๋ชป ์ ์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•˜์ž.

     

    ์ ‘์†ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜์˜จ๋‹ค. ๋กœ๊ทธ์ธ ํ•ด์ฃผ์ž

    ๋กœ๊ทธ์ธ ์ž˜ ๋๋‚ด์ฃผ๋ฉด

    ์ด๋Ÿฐ ์ฐฝ์ด ๋œฌ๋‹ค. ์‚ฌ์ง„์—๋Š” ์ž˜๋ ธ๋‹ค๋งŒ ์œ„์˜ c์— ํ•ด๋‹นํ•˜๋Š” code๊ฐ€ ๋’ค์— ์ฟผ๋ฆฌ๋กœ ๋ถ™์–ด์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    IntelliJ ๋กœ๊ทธ์— ์›ํ•˜๋Š” ์ •๋ณด๊ฐ€ ์ž˜ ๋œจ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

    (๋ฐ์ดํ„ฐ๋Š” ๋‚ด๊ฐ€ id๋งŒ ๋ฐ›์•„์˜ค๋„๋ก ์„ค์ •ํ•ด์„œ ์ €๋ ‡๋‹ค. ํ•„์š”ํ•˜๋‹ค๋ฉด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

    id๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋‹จ์œ„๋กœ ์ค‘๋ณต์ด ์—†๋‹ค๊ณ  ํ•œ๋‹ค. https://devtalk.kakao.com/t/client-id/123333

     

    ์นด์นด์˜ค ๋กœ๊ทธ์ธ์—์„œ ์ œ๊ณต๋˜๋Š” ํšŒ์›๋ฒˆํ˜ธ๋Š” client_id ๋ณ„๋กœ ๋‹ฌ๋ผ์ง€๋‚˜์š”?

    ์œ„ ์„ค๋ช…์— ๋”ฐ๋ฅด๋ฉด ์„œ๋น„์Šค ๋ณ„๋กœ ๋™์ผ์ธ์˜ ํšŒ์›๋ฒˆํ˜ธ๊ฐ€ ๋‹ฌ๋ผ์งˆ ๊ฒƒ ๊ฐ™์€๋ฐ ์‚ฌ์šฉ์ž ๊ณ„์ • ๊ธฐ์ค€์œผ๋กœ ์œ ์ผํ•œ 1๊ฐœ(n๊ฐœ์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋™์ผํ•œ ๊ฐ’)๊ฐ€ ์•„๋‹ˆ๋ผ ๋“ฑ๋ก๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณ„๋กœ ์‚ฌ์šฉ์ž ๊ณ„์ •์—

    devtalk.kakao.com

    ๊ทธ๋ž˜์„œ ์ „ DB์— user column์œผ๋กœ social_id๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์ €์žฅํ•˜๊ณค ํ•ฉ๋‹ˆ๋‹ค.

     

    ์ง„์งœ ์นด์นด์˜ค ๋กœ๊ทธ์ธ API ๋งŒ๋“ค๊ธฐ

    ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค๋„๋ก ํ•˜๊ฒ ๋‹ค.

    ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ๊ฐ์ž์˜ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋ฐ”๊ฟ”์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ๋งŽ์„ ๊ฒƒ ๊ฐ™๋‹ค.

    ์ฐธ๊ณ ๋กœ ์ €๋Š” ๋ณดํ†ต Social Type์„ ์—ฌ๋Ÿฌ๊ฐœ ์ง€์›ํ•ด์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ €๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์งœ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    ํ†ต์‹  ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์งœ๊ณ  ์†Œ์…œ ํƒ€์ž…์„ enum์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ํ†ต์‹ ํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹๋„ ์žˆ์œผ๋‹ˆ ์‹œ๊ฐ„ ๋˜์‹œ๋ฉด ํ•จ ํ•ด๋ณด์„ธ์—ฌ

     

    ์—ฌํŠผ ์•„์˜ˆ user id๋งŒ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋ฐ”๊ฟจ๋‹ค.

    public String getUserInfoId(String accessToken) {
    
        String reqURL = "https://kapi.kakao.com/v2/user/me";
        String response;
        try {
            //accessToken์„ ํ—ค๋”๋กœ ๋ถ™์—ฌ์„œ ํ†ต์‹ 
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Authorization", "Bearer " + accessToken);
    
            JsonElement element = JsonParser.parseString(getConnectionResponse(conn.getInputStream()));
            response = element.getAsJsonObject().get("id").getAsString();
        } catch (IOException e) {
            throw new CustomException(FORBIDDEN_TOKEN_NOT_VALID);
        }
    
        return response;
    }
    @PostMapping //url์€ ํ•„์š”ํ•œ๋Œ€๋กœ ๋ฐ”๊พธ๊ธฐ
    public ResponseEntity socialLogin(@RequestHeader("Authorization") String token) {
        return ResponseDataTemplate.toResponseEntity(
                OK_SUCCESS,
                oAuthService.kakaoLogin(token)); //์‘๋‹ต๋„ ํ•„์š”ํ•œ๋Œ€๋กœ ๋ฐ”๊พธ๊ธฐ
    }
    @Transactional
    public LoginResponseDto kakaoLogin(String oAuthToken) {
        String socialId = getUserInfoId(oAuthToken);
    
    	//ํ•ด๋‹น social id๋ฅผ ๊ฐ€์ง„ ํšŒ์› ์กด์žฌ ์—ฌ๋ถ€
        User user = userRepository.findBySocialId(socialId)
        //์—†์œผ๋ฉด ์ƒˆ๋กœ์šด ์œ ์ € ์ƒ์„ฑ. ๋ณดํ†ต์€ ์—ฌ๊ธฐ์„œ ํšŒ์›๊ฐ€์ž…์œผ๋กœ ๋„˜์–ด๊ฐ€๋ผ๋Š” ์‹ ํ˜ธ๋ฅผ ๋ฆฌํ„ดํ•˜๊ธฐ๋„ ํ•จ
                .orElse(new User(socialId));
        userRepository.save(user);
    
    	//user jwt ํ† ํฐ ์ƒ์„ฑํ•ด์„œ dto์— ๋„ฃ๊ณ  return;
        return new LoginResponseDto(jwtTokenProvider.createToken(user));
    }

     

Designed by Tistory.