Overview
- Google API
- Kakao API
- Naver API
Google API cloud
https://cloud.google.com/apis?hl=ko
https://cloud.google.com/apis?hl=ko
cloud.google.com
Kakao API Developer
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
Naver API Developer
https://developers.naver.com/main/
NAVER Developers
네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음
developers.naver.com
뭐 redirect 설정이나 그런것은 다른쪽에서 찾아보셨다는 가정하에
바로 Springboot
전에 프론트에서 이쪽으로 연결시켜 주세요
클라이언트 id 와 redirect 는 홈페이지에 있을겁니다.
google
https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?client_id=${googleClientId}&redirect_uri=${googleRedirectUrl}&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&serviso&o2v=1&theme=glif&flowName=GeneralOAuthFlow
kakao
https://kauth.kakao.com/oauth/authorize?client_id=${kakaoClientId}&redirect_uri=${kakaoRedirectUrl}&response_type=code
그럼 Springboot 로 들어가보죠
build.gradle
// Todo : OAuth2
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
// Todo : webclient
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.13'
OAuth2 를 쓰냐구요? 몰라요 쓰는건가 안쓰는건가
아마 쓰겠죠 ㅎㅎ...
application.properties
# Todo : OAuth2 : google
oauth2.google.client-id:
oauth2.google.client-secret:
oauth2.google.redirect-uri:
oauth2.google.token-uri:https://oauth2.googleapis.com/token
oauth2.google.resource-uri:https://www.googleapis.com/oauth2/v2/userinfo
oauth2.google.user:https://www.googleapis.com/userinfo/v2/me
# Todo : OAuth2 : Kakao
oauth2.kakao.client-id:
oauth2.kakao.client-secret:
oauth2.kakao.redirect-uri:
oauth2.kakao.token-uri:https://kauth.kakao.com/oauth/token
oauth2.kakao.authorization-uri:https://kauth.kakao.com/oauth/authorize
oauth2.kakao.user:https://kapi.kakao.com/v2/user/me
만약 프론트에서 로그인을 성공 했다면 code를 주게 됩니다
일단 Controller 부터 볼까요?
url은 프로젝트에 맞는 설정을 해주세요
// Todo : OAuth2 로그인
@GetMapping("/auth/login/oauth2/code/{registrationId}")
public ResponseEntity<Object> socialLogin(@RequestParam String code, @PathVariable String registrationId) {
try {
UserDto user = authService.socialLogin(code, registrationId);
return new ResponseEntity<>(HttpStatus.OK);
} catch (Exception e) {
log.info(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Service 입니다.
쓰실려면 수정을 거쳐야 될거예요
@Service
@Slf4j
public class AuthService {
@Autowired
Environment env;
RestTemplate restTemplate = new RestTemplate();
// Todo : OAuth2 서비스
public UserDto socialLogin(String code, String registrationId) {
// Todo : 구글 api
if(registrationId.equals("google")) {
String accessToken = getAccessToken(code, registrationId);
UserDto user = getUser(accessToken, registrationId);
return user;
// Todo : 카카오 api
} else {
JSONObject jsonObject = getKakaoAccessToken(code, registrationId);
UserDto userDto = getKakaoUser(jsonObject, registrationId);
return userDto;
}
}
}
1. Google API
따로따로 구글부터 보죠
restTemplate 를 사용 했습니다.
// Todo : 구글 api
// Todo : accessToken 값 가져오기
private String getAccessToken(String authorizationCode, String registrationId) {
String clientId = env.getProperty("oauth2." + registrationId + ".client-id");
String clientSecret = env.getProperty("oauth2." + registrationId + ".client-secret");
String redirectUri = env.getProperty("oauth2." + registrationId + ".redirect-uri");
String tokenUri = env.getProperty("oauth2." + registrationId + ".token-uri");
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("code", authorizationCode);
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
params.add("redirect_uri", redirectUri);
params.add("grant_type", "authorization_code");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity entity = new HttpEntity(params, headers);
ResponseEntity<JsonNode> responseNode = restTemplate.exchange(tokenUri, HttpMethod.POST, entity, JsonNode.class);
JsonNode accessTokenNode = responseNode.getBody();
return accessTokenNode.get("access_token").asText();
}
// Todo : 유저 정보 가져오기
public UserDto getUser(String accessToken, String registrationId) {
String baseUrl = env.getProperty("oauth2." + registrationId + ".user");
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(baseUrl)
.queryParam("access_token", accessToken)
.build(false); // 인코딩 하지않음
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
ResponseEntity<User> response = restTemplate.exchange(uriComponents.toUriString(), HttpMethod.GET, new HttpEntity<String>(headers), User.class);
User user = response.getBody();
// Todo : 유저 정보가 있으면 바로 주기
if (!userRepository.existsByEmail(user.getEmail())) {
// Todo : 없으면 회원가입
user.setRole(Role.ROLE_USER.name());
user.setPoint(0);
siginup(user);
}
return selectByEmail(user.getEmail()).get();
}
아마 먼가 안맞네? 하는 부분은 제가 프로젝트에서 변형해서 썻기 때문입니다 ㅎㅎㅎ
2. Kakao API
다음은 kakao 입니다.
WebClient를 사용했습니다.
// Todo : 카카오 api
private JSONObject getKakaoAccessToken(String authorizationCode, String registrationId) {
String clientId = env.getProperty("oauth2." + registrationId + ".client-id");
String clientSecret = env.getProperty("oauth2." + registrationId + ".client-secret");
String redirectUri = env.getProperty("oauth2." + registrationId + ".redirect-uri");
String tokenUri = env.getProperty("oauth2." + registrationId + ".token-uri");
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("code", authorizationCode);
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
params.add("redirect_uri", redirectUri);
params.add("grant_type", "authorization_code");
return WebClient.builder()
.baseUrl(tokenUri)
.build()
.post()
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
.body(BodyInserters.fromFormData(params))
.retrieve()
.bodyToMono(JSONObject.class)
.block();
}
// Todo : 유저 정보 가져오기
public UserDto getKakaoUser(JSONObject tokenInfo, String registrationId) {
// [STEP2] 인가 코드를 기반으로 토큰을 발급받습니다.
String accessToken = String.valueOf(tokenInfo.get("access_token"));
int refreshTokenExpiresIn = (int) tokenInfo.get("refresh_token_expires_in");
String refreshToken = String.valueOf(tokenInfo.get("refresh_token"));
String scope = String.valueOf(tokenInfo.get("scope"));
// [STEP3] 접근 코드를 기반으로 사용자 정보를 조회합니다.
JSONObject userInfo = findUserInfo(accessToken, registrationId);
String kakaoAccount = String.valueOf(userInfo.get("kakao_account"));
try {
Map<String, Object> account = (Map<String, Object>) userInfo.get("kakao_account");
Map<String, Object> profile = (Map<String, Object>) account.get("profile");
String email = (String) account.get("email");
String name = (String) profile.get("nickname");
String profile_image_url = (String) profile.get("profile_image_url");
if (!userRepository.existsByEmail(email)) {
siginup(User.builder().name(name).email(email).role(Role.ROLE_USER.name()).point(0).build());
}
return userRepository.selectByEmail(email).get();
} catch (Exception e) {
log.info(e.getMessage());
return null;
}
}
private JSONObject findUserInfo(String accessToken, String registrationId) {
// 전달 받은 토큰을 기반으로 사용자 정보를 조회합니다.
String baseUrl = env.getProperty("oauth2." + registrationId + ".user");
return WebClient.builder()
.baseUrl(baseUrl)
.build()
.post()
.header("Content-type", "application/x-www-form-urlencoded;charset=utf-8")
.header("Authorization", "Bearer " + accessToken)
.retrieve()
.bodyToMono(JSONObject.class)
.block();
}
번외. Kakao API 로그인 + RestTemplate
@GetMapping("/kakao-callback")
public String kakaoCallBack(@RequestParam String code) {
// Post 방식 , HEADER 구성 , BODY 구성
RestTemplate rt1 = new RestTemplate();
// 헤더 구성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
// 바디 구성 : MultiValueMap
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("grant_type", "authorization_code");
params.add("client_id", "");
params.add("redirect_uri", "");
params.add("code", code);
// 헤더 + 바디 결합
HttpEntity<MultiValueMap<String, String>> reqMsg = new HttpEntity<>(params, headers);
ResponseEntity<OAuthToken> response = rt1.exchange("https://kauth.kakao.com/oauth/token", HttpMethod.POST,
reqMsg, OAuthToken.class);
// 다시 요청 - 인증 토큰을 -- 사용자 정보 요청
String accessToken = response.getBody().getAccessToken();
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<MultiValueMap<String, String>> reqMsg2 = new HttpEntity<>(headers);
ResponseEntity<KakaoProfile> response2 = rt1.exchange("https://kapi.kakao.com/v2/user/me", HttpMethod.GET,
reqMsg2, KakaoProfile.class);
KakaoProfile kakaoProfile = response2.getBody();
String username = kakaoProfile.getProperties().getNickname();
return "redirect:/account/list";
}
번외. Naver API 로그인 + JSP
1. JSP
먼저 JSP 부분입니다. 이는 문서에 있기 때문에 따로 설명은 없습니다.
1. naverlogin.jsp
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.security.SecureRandom" %>
<%@ page import="java.math.BigInteger" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>네이버로그인</title>
</head>
<body>
<%
String clientId = "YOUR_CLIENT_ID";//애플리케이션 클라이언트 아이디값";
String redirectURI = URLEncoder.encode("YOUR_CALLBACK_URL", "UTF-8");
SecureRandom random = new SecureRandom();
String state = new BigInteger(130, random).toString();
String apiURL = "https://nid.naver.com/oauth2.0/authorize?response_type=code";
apiURL += "&client_id=" + clientId;
apiURL += "&redirect_uri=" + redirectURI;
apiURL += "&state=" + state;
session.setAttribute("state", state);
%>
<a href="<%=apiURL%>"><img height="50" src="http://static.nid.naver.com/oauth/small_g_in.PNG"/></a>
</body>
</html>
2. Springboot
다음은 Springboot 부분입니다. 토큰을 발급하고, 필요한 회원 정보를 가져옵니다.
URI + UriComponentsBuilder 를 사용했습니다. (MultiValueMap 은 사용이 안되네요 왜지)
파라미터 값
2. API 기본 정보
API 기본 정보 설명 표메서드인증요청 URL출력 포맷설명
GET | OAuth 2.0 | https://openapi.naver.com/v1/nid/me | JSON | 네이버 회원 프로필 조회 |
3. 요청 변수
요청 변수는 별도로 없으며, 요청 URL로 호출할 때 아래와 같이 요청 헤더에 접근 토큰 값을 전달하면 됩니다.
4. 요청 헤더
요청 헤더 설명 표요청 헤더명설명
Authorization | 접근 토큰(access token)을 전달하는 헤더 다음과 같은 형식으로 헤더 값에 접근 토큰(access token)을 포함합니다. 토큰 타입은 "Bearer"로 값이 고정돼 있습니다. Authorization: {토큰 타입] {접근 토큰] |
요청 헤더 예
Authorization: Bearer AAAAOLtP40eH6P5S4Z4FpFl77n3FD5I+W3ost3oDZq/nbcS+7MAYXwXbT3Y7Ib3dnvcqHkcK0e5/rw6ajF7S/QlJAgUukpp1OG
5. Example
// 네이버 로그인
@GetMapping("/naver-callback")
public String naverCallback(@RequestParam String code, @RequestParam String state) {
RestTemplate restTemplate = new RestTemplate();
// uri 헤더 없음 , 파라미터만 있음 ㅎㅎ..
URI uri = UriComponentsBuilder.fromUriString("https://nid.naver.com").path("/oauth2.0/token")
.queryParam("grant_type", "authorization_code").queryParam("client_id", "")
.queryParam("client_secret", "").queryParam("code", code).queryParam("state", state).encode()
.build().toUri();
// 토큰 가져오기
ResponseEntity<NaverTokenDto> response = restTemplate.getForEntity(uri, NaverTokenDto.class);
// 토큰 정보 빼내기
// 헤더 추가
HttpHeaders headers = new HttpHeaders();
String accessToken = response.getBody().getAccessToken();
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<Object> entity = new HttpEntity<>(headers);
ResponseEntity<NaverProfile> response2 = restTemplate.exchange("https://openapi.naver.com/v1/nid/me", HttpMethod.GET,
entity, NaverProfile.class);
NaverProfile naverProfile = response2.getBody();
return "redirect:/account/list";
}
'SpringBoot > code' 카테고리의 다른 글
WebSocket 을 사용해보자 - 2. Stomp Springboot 부분 (2) | 2024.01.04 |
---|---|
WebSocket 을 사용해보자 - 2. Stomp 프론트 부분 (1) | 2024.01.04 |
SpringBoot - MyBatis - 다이나믹 SQL 작성 (0) | 2023.10.16 |
SpringBoot - SSR에서 페이징 처리에 필요한 객체 PageReq , PageRes (0) | 2023.10.12 |
SpringBoot - model / common package (0) | 2023.10.12 |