[Spring Boot]네이버 로그인 구현
※ 테스트 환경
- Spring Boot
- JDK 1.8
- Gradle
- Thymeleaf
1. 네이버 개발자 설정
1) 애플리케이션 추가
Naver Developers 에 접속해 애플리케이션을 추가해줍니다.
여기서 Client ID와 Client Secret 키가 생성되고 API 호출시 사용하게 됩니다.
2) API 설정
메뉴 - API 설정에 들어와 제공받을 정보를 선택해줍니다. 본 예제에서는 이름과 이메일을 사용했습니다.
3) 로그인 환경 추가
PC 웹 환경을 추가하고 테스트 URL을 입력합니다.
2. 로그인 구현
네이버 로그인도 카카오 로그인과 동일하 동작합니다. 네이버 인증 서버로부터 인증 코드를 내려받고 인증 코드를 이용해 액세스 토큰을 요청합니다. 그리고 네이버 인증 서버로부터 내려받은 액세스 토큰으로 사용자 정보를 가져옵니다.
- 인증 코드 요청
- 액세스 토큰을 이용해 사용자 정보 가져오기
1) 인증 코드 요청
1-1) application.properties 정의
# 네이버 로그인 설정
naver.client.id=8Mg0VNWj3Q3UP5c5K4ek
naver.client.secret=eVuIa8YUrO
naver.redirect.url=http://localhost:7070/naver/callback
1-2) Controller 생성
@RequiredArgsConstructor
@Controller
public class HomeController {
private final AppleService appleService;
private final KakaoService kakaoService;
private final NaverService naverService;
@RequestMapping(value="/", method= RequestMethod.GET)
public String login(Model model) {
model.addAttribute("appleUrl", appleService.getAppleLogin());
model.addAttribute("kakaoUrl", kakaoService.getKakaoLogin());
model.addAttribute("naverUrl", naverService.getNaverLogin());
return "index";
}
}
1-3) Service 생성
@Service
public class NaverService {
@Value("${naver.client.id}")
private String NAVER_CLIENT_ID;
@Value("${naver.client.secret}")
private String NAVER_CLIENT_SECRET;
@Value("${naver.redirect.url}")
private String NAVER_REDIRECT_URL;
private final static String NAVER_AUTH_URI = "https://nid.naver.com";
private final static String NAVER_API_URI = "https://openapi.naver.com";
public String getNaverLogin() {
return NAVER_AUTH_URI + "/oauth2.0/authorize"
+ "?client_id=" + NAVER_CLIENT_ID
+ "&redirect_uri=" + NAVER_REDIRECT_URL
+ "&response_type=code";
}
}
1-4) index.html 생성
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<title>소셜로그인 예제</title>
</head>
<body>
<div>
<p><a th:href="@{|${appleUrl}|}">애플로그인</a></p>
<p><a th:href="@{|${kakaoUrl}|}">카카오로그인</a></p>
<p><a th:href="@{|${naverUrl}|}">네이버로그인</a></p>
</div>
</body>
</html>
2) 액세스 토큰 및 유저 정보 요청
이제 인증 코드를 전달받을 redirect_uri에 매핑될 Controller를 만들어보겠습니다.
2-1) Controller 생성
@RestController
@RequiredArgsConstructor
@RequestMapping("naver")
public class NaverController {
private final NaverService naverService;
@GetMapping("/callback")
public ResponseEntity<MsgEntity> callback(HttpServletRequest request) throws Exception {
NaverDTO naverInfo = naverService.getNaverInfo(request.getParameter("code"));
return ResponseEntity.ok()
.body(new MsgEntity("Success", naverInfo));
}
}
2-2) Service 수정
@Service
public class NaverService {
@Value("${naver.client.id}")
private String NAVER_CLIENT_ID;
@Value("${naver.client.secret}")
private String NAVER_CLIENT_SECRET;
@Value("${naver.redirect.url}")
private String NAVER_REDIRECT_URL;
private final static String NAVER_AUTH_URI = "https://nid.naver.com";
private final static String NAVER_API_URI = "https://openapi.naver.com";
public String getNaverLogin() {
return NAVER_AUTH_URI + "/oauth2.0/authorize"
+ "?client_id=" + NAVER_CLIENT_ID
+ "&redirect_uri=" + NAVER_REDIRECT_URL
+ "&response_type=code";
}
public NaverDTO getNaverInfo(String code) throws Exception {
if (code == null) throw new Exception("Failed get authorization code");
String accessToken = "";
String refreshToken = "";
try {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded");
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type" , "authorization_code");
params.add("client_id" , NAVER_CLIENT_ID);
params.add("client_secret", NAVER_CLIENT_SECRET);
params.add("code" , code);
params.add("redirect_uri" , NAVER_REDIRECT_URL);
RestTemplate restTemplate = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.exchange(
NAVER_AUTH_URI + "/oauth2.0/token",
HttpMethod.POST,
httpEntity,
String.class
);
JSONParser jsonParser = new JSONParser();
JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());
accessToken = (String) jsonObj.get("access_token");
refreshToken = (String) jsonObj.get("refresh_token");
} catch (Exception e) {
throw new Exception("API call failed");
}
return getUserInfoWithToken(accessToken);
}
private NaverDTO getUserInfoWithToken(String accessToken) throws Exception {
//HttpHeader 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpHeader 담기
RestTemplate rt = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = rt.exchange(
NAVER_API_URI + "/v1/nid/me",
HttpMethod.POST,
httpEntity,
String.class
);
//Response 데이터 파싱
JSONParser jsonParser = new JSONParser();
JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());
JSONObject account = (JSONObject) jsonObj.get("response");
String id = String.valueOf(account.get("id"));
String email = String.valueOf(account.get("email"));
String name = String.valueOf(account.get("name"));
return NaverDTO.builder()
.id(id)
.email(email)
.name(name).build();
}
}
추가된 메소드는 총 2개입니다.
getNaverInfo(String code)
getUserInfoWithToken(String accessToken)
getNaverInfo(String code)는 컨트롤러에서 리턴받은 인증 코드값을 통해 네이버인증 서버에 액세스 토큰을 요청합니다. 토큰은 액세스 토큰과 리프레쉬 토큰 두가지가 있는데, 리프레쉬 토큰은 액세스 토큰을 갱신할 때 사용됩니다. 액세스 토큰은 만료 시간이 존재하기 때문에 발급받은 리프레쉬 토큰을 저장해두고 액세스 토큰을 갱신하는 형태로 사용하게 됩니다. 본 예제에서는 발급받은 액세스 토큰을 통해 사용자 정보를 가져오는데까지만 다루고 있습니다.
getUserInfoWithToken(String accessToken)은 전달받은 액세스 토큰을 통해 사용자 정보를 가져옵니다. 사용자 정보는 사용자가 동의한 내역에 한해서 가져올 수 있습니다. 각 정보의 응답 키는 문서에 자세하게 나와있습니다.
RestTemplate을 이용해 https://openapi.naver.com/oauth2.0/token 주소로 토큰을 요청해봅니다.
RestTemplate restTemplate = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.exchange(
NAVER_AUTH_URI + "/oauth2.0/token",
HttpMethod.POST,
httpEntity,
String.class
);
JSONParser jsonParser = new JSONParser();
JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());
accessToken = (String) jsonObj.get("access_token");
refreshToken = (String) jsonObj.get("refresh_token");
액세스 토큰을 정상적으로 발급받았다면 이제 사용자 정보를 가져올 수 있습니다. 이 때 Header에 Bearer 액세스 토큰 을 담아 요청합니다.
//HttpHeader 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
//HttpHeader 담기
RestTemplate rt = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = rt.exchange(
NAVER_API_URI + "/v1/nid/me",
HttpMethod.POST,
httpEntity,
String.class
);
//Response 데이터 파싱
JSONParser jsonParser = new JSONParser();
JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());
JSONObject account = (JSONObject) jsonObj.get("response");
응답받은 Response 안에는 id값이 존재하는데 이것은 회원 고유의 값입니다. 카카오와 다르게 response라는 키 안에 존재합니다.
2-3) DTO 생성
@Builder
@Data
public class NaverDTO {
private String id;
private String email;
private String name;
}
2-4) 결과
참고문서
+ 피드백은 언제나 환영입니다 :)
전체 소스는 GitHub에서 확인하실 수 있습니다.