-
[๊ตฌํ] 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)); }
'SPRING > PROJECT' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ