로그인 페이지 컴포넌트
import LoginNaver from './LoginNaver';
<LoginNaver />
네이버 로그인 JDK 사용로직
import React, { useEffect } from 'react';
import { naverClientId, naverRedirectURL, naverSecret } from '../../utils/OAuth';
const { naver } = window;
const LoginNaver = () => {
const initializeNaverLogin = () => {
const naverLogin = new naver.LoginWithNaverId({
clientId: naverClientId,
callbackUrl: naverRedirectURL,
clientSecret: naverSecret,
isPopup: false, // popup 형식으로 띄울것인지 설정
loginButton: { color: 'green', type: 3, height: '60' }, //버튼의 스타일, 타입, 크기를 지정
});
naverLogin.init();
};
useEffect(() => {
initializeNaverLogin();
}, []);
return <div id='naverIdLogin' />;
};
export default LoginNaver;
반환값
http//리다이렉트 설정 url ? access-token = {토큰값} & state = {스테이트 값} & token-type = {토근타입} & expire = {만료기간}
a태그에 https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id={클라이언트아이디}&state={임의값 사용}&redirect_uri={등록한 리다이렉트 URL}
를 넣어 네이버로그인페이지로 이동시켜줍니다. 그리고 로그인이 완료되면 설정해놓은 리다이렉트 주소로 URL에 code값과 state값이 넘어옵니다.
네이버 JDK로 로그인을 클라이언트에서 하게되면 리다이렉트로 넘겨주는 토큰은 일회성토큰이 아니라 바로 사용할 수 있는 접근토큰입니다.
따라서 서버에서 사용자 프로필정보를 바로 요청할 수 있습니다.
(이거 몰라서 해당 토큰으로 토큰발급 api 작동해서 엄청 고생했습니다...)
반대로 REST API로 클라이언트에서 요청한 경우에는 일회성토큰이므로 서버에서 한번 더 토큰발급을 해당 일회성 토큰으로 발급하고 프로필정보요청 API를 사용해야합니다.
네이버 로그인 JDK 서버 처리 로직 (Spring)
public void naverToken(String code, HttpServletResponse response) throws IOException {
try {
JSONParser jsonParser = new JSONParser();
String header = "Bearer " + code;
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Authorization", header);
String responseBody = get(NAVER_USER_INFO_URI, requestHeaders);
JSONObject parse = (JSONObject) jsonParser.parse(responseBody);
JSONObject responseParse = (JSONObject) parse.get("response");
String encodeUserName = (String) responseParse.get("name");
String loginId = (String) responseParse.get("id");
String email = (String) responseParse.get("email");
String phoneNumber = (String) responseParse.get("mobile_e164");
String userName = new String(encodeUserName.getBytes(StandardCharsets.UTF_8));
User user = new UserRequest("social_" + loginId, userName, encode.encode("네이버"), email, phoneNumber).naverOAuthToEntity();
if (userRepository.existsByLoginId(user.getLoginId()) == false) {
userRepository.save(user);
}
String access_token = tokenProvider.create(new PrincipalDetails(user));
response.addHeader("Authorization", "Bearer " + access_token);
} catch (Exception e) {
e.printStackTrace();
}
}
카카오는 REST API로만 구현해서 해당 부분만 포스팅하겠습니다.
카카오 REST API 클라이언트 사용로직
// 변수처리를 위한 다른 JS 파일
export const KAKAO_AUTH_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoClientId}&redirect_uri=${kakaoRedirectURL}&response_type=code&prompt=login`;
// 로그인 페이지 해당 코드
<KakaoBtn href={KAKAO_AUTH_URL}>
<img src={`${process.env.PUBLIC_URL}/images/kakao_login_large_narrow.png`} alt='kakaoLogin' />
</KakaoBtn>
반환값
http//리다이렉트 설정 url?code={일회성 토큰값}
별다른 state값을 로그인 요청시 url에 넣어주지 않았다면 state값은 선택사항이라 넘어오지 않을 것이다. 문서보고 헷갈리지 말자!
이부분은 REST API를 사용했기 때문에 일회성토큰이 클라이언트로 넘어옵니다. 따라서 해당 토큰을 가지고 서버에서 한번 더 정식 토큰 발급을 요청하고 넘어온 access-token으로 사용자프로필 조회 API를 작동해야 오류가 나지 않습니다!!
또한, contentType이 올바른지 꼭 확인하세요!
제 경우에는 ContentType -> application/json
로 멋대로 json으로 요청을 보냈다가
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]
위와 같은 에러가 났으니 꼭 !!!
ContentType은 application/x-www-form-urlencoded;charset=utf-8
공식문서대로 진행해서 요청해주세요!!
카카오 로그인 Rest API 서버 처리 로직 (Spring)
public void kakaoToken(String code, HttpServletResponse res, HttpSession session) {
RestTemplate rt = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
MultiValueMap<String, String> accessTokenParams = accessTokenParams("authorization_code",KAKAO_CLIENT_ID ,code,KAKAO_REDIRECT_URI);
HttpEntity<MultiValueMap<String, String>> accessTokenRequest = new HttpEntity<>(accessTokenParams, headers);
ResponseEntity<String> accessTokenResponse = rt.exchange(
KAKAO_TOKEN_URI,
HttpMethod.POST,
accessTokenRequest,
String.class);
try {
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(accessTokenResponse.getBody());
session.setAttribute("Authorization", jsonObject.get("access_token"));
String header = "Bearer " + jsonObject.get("access_token");
System.out.println("header = " + header);
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Authorization", header);
String responseBody = get(KAKAO_USER_INFO_URI, requestHeaders);
JSONObject profile = (JSONObject) jsonParser.parse(responseBody);
JSONObject properties = (JSONObject) profile.get("properties");
JSONObject kakao_account = (JSONObject) profile.get("kakao_account");
Long loginId = (Long) profile.get("id");
String email = (String) kakao_account.get("email");
String userName = (String) properties.get("nickname");
User kakaoUser = new UserRequest("social_" + loginId, encode.encode("카카오"), userName, email).kakaoOAuthToEntity();
if (userRepository.existsByLoginId(kakaoUser.getLoginId()) == false) {
userRepository.save(kakaoUser);
}
String access_token = tokenProvider.create(new PrincipalDetails(kakaoUser));
res.setHeader("Authorization", "Bearer "+access_token);
} catch (Exception e) {
e.printStackTrace();
}
}
결국 모든 것은 설정만 잘하고 사용하면 문제가 생길리 없다.... ✨