스프링프레임워크 환경에서 네이버, 카카오 아이디로 로그인하기 전체 소스 입니다.
다른 포스팅 참조해서 개발했고, 다른 포스팅에서 잘못된 부분도 있는것 같아서 보완 했습니다.
네이버 아이디로 로그인 하기와 카카오 아이디로 로그인하기는 둘 다 OAuth2.0 프로토콜이기 때문에 동작 방식도 똑같고 소스도 동일하다는 점을 착안하면 이해하는데 도움이 될 것 같습니다.
그리고 구글, 페이스북도 동일한 OAuth2.0 프로토콜이기 때문에 아래 소스로 사용하면 될 것 같습니다.
login.jsp
<div>
<a href="${naverAuthUrl}">
<img width="230" height="53" src="<c:url value='/resources/img/btn_naver.png'/>">
</a>
<a href="${kakaoAuthUrl}">
<img width="230" height="53" src="<c:url value='/resources/img/btn_kakao.png'/>">
</a>
</div>
ControllerLogin.java
@Controller
@RequestMapping("/login")
public class ControllerLogin {
@Autowired
NaverLoginBO naverLoginBO;
@Autowired
KakaoLoginBO kakaoLoginBO;
// 로그인 화면
@RequestMapping(value = "/login.do")
public String login(HttpServletRequest request, HttpServletResponse response, Model model, HttpSession session) {
String serverUrl = request.getScheme()+"://"+request.getServerName();
if(request.getServerPort() != 80) {
serverUrl = serverUrl + ":" + request.getServerPort();
}
String naverAuthUrl = naverLoginBO.getAuthorizationUrl(session, serverUrl);
model.addAttribute("naverAuthUrl", naverAuthUrl);
String kakaoAuthUrl = kakaoLoginBO.getAuthorizationUrl(session, serverUrl);
model.addAttribute("kakaoAuthUrl", kakaoAuthUrl);
return "/login/login";
}
// 네이버 로그인 성공시 callback
@RequestMapping(value = "/naverOauth2ClientCallback.do", method = { RequestMethod.GET, RequestMethod.POST })
public String naverOauth2ClientCallback(HttpServletRequest request, HttpServletResponse response, Model model, @RequestParam String code, @RequestParam String state, HttpSession session) throws Exception {
String serverUrl = request.getScheme()+"://"+request.getServerName();
if(request.getServerPort() != 80) {
serverUrl = serverUrl + ":" + request.getServerPort();
}
OAuth2AccessToken oauthToken;
oauthToken = naverLoginBO.getAccessToken(session, code, state, serverUrl);
if(oauthToken == null) {
model.addAttribute("msg", "네이버 로그인 access 토큰 발급 오류 입니다.");
model.addAttribute("url", "/");
return "/common/redirect";
}
// 로그인 사용자 정보를 읽어온다
String apiResult = naverLoginBO.getUserProfile(oauthToken, serverUrl);
JSONParser jsonParser = new JSONParser();
Object obj = jsonParser.parse(apiResult);
JSONObject jsonObj = (JSONObject) obj;
JSONObject response_obj = (JSONObject) jsonObj.get("response");
// 프로필 조회
String id = (String) response_obj.get("id");
String gender = (String) response_obj.get("gender");
// 세션에 사용자 정보 등록
session.setAttribute("islogin_r", "Y");
session.setAttribute("id", id);
session.setAttribute("gender", gender);
return "redirect:/";
}
// 카카오 로그인 성공시 callback
@RequestMapping(value = "/kakaoOauth2ClientCallback.do", method = { RequestMethod.GET, RequestMethod.POST })
public String kakaoOauth2ClientCallback(HttpServletRequest request, HttpServletResponse response, Model model, @RequestParam String code, @RequestParam String state, HttpSession session) throws Exception {
String serverUrl = request.getScheme() + "://" + request.getServerName();
if (request.getServerPort() != 80) {
serverUrl = serverUrl + ":" + request.getServerPort();
}
OAuth2AccessToken oauthToken;
oauthToken = kakaoLoginBO.getAccessToken(session, code, state, serverUrl);
if (oauthToken == null) {
model.addAttribute("msg", "카카오 로그인 access 토큰 발급 오류 입니다.");
model.addAttribute("url", "/");
return "/common/redirect";
}
// 로그인 사용자 정보를 읽어온다
String apiResult = kakaoLoginBO.getUserProfile(oauthToken, serverUrl);
JSONParser jsonParser = new JSONParser();
Object obj = jsonParser.parse(apiResult);
JSONObject jsonObj = (JSONObject) obj;
JSONObject response_obj = (JSONObject) jsonObj.get("kakao_account");
// 프로필 조회
String id = (String) response_obj.get("id");
String gender = (String) response_obj.get("gender");
// 세션에 사용자 정보 등록
session.setAttribute("islogin_r", "Y");
session.setAttribute("id", id);
session.setAttribute("gender", gender);
return "redirect:/";
}
// 로그아웃
@RequestMapping(value = "/logout.do")
public String logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {
// 세션 삭제
session.invalidate();
return "redirect:/";
}
}
NaverLoginBO.java
@Component
public class NaverLoginBO {
// 네이버 로그인 정보
private final static String NAVER_CLIENT_ID = "E9oOK2dP72DRQLg4zFB9";
private final static String NAVER_CLIENT_SECRET = "당신의 시크리트 키";
private final static String NAVER_REDIRECT_URI = "/login/naverOauth2ClientCallback.do";
private final static String SESSION_STATE = "naver_oauth_state";
private final static String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";
public String getAuthorizationUrl(HttpSession session, String serverUrl) {
String state = generateRandomString();
setSession(session, state);
OAuth20Service oauthService = new ServiceBuilder().apiKey(NAVER_CLIENT_ID).apiSecret(NAVER_CLIENT_SECRET).callback(serverUrl + NAVER_REDIRECT_URI).state(state).build(NaverOAuthApi20.instance());
return oauthService.getAuthorizationUrl();
}
public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state, String serverUrl) throws Exception {
String sessionState = getSession(session);
if (StringUtils.equals(sessionState, state)) {
OAuth20Service oauthService = new ServiceBuilder().apiKey(NAVER_CLIENT_ID).apiSecret(NAVER_CLIENT_SECRET).callback(serverUrl + NAVER_REDIRECT_URI).state(state).build(NaverOAuthApi20.instance());
OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
return accessToken;
}
return null;
}
public String getUserProfile(OAuth2AccessToken oauthToken, String serverUrl) throws Exception {
OAuth20Service oauthService = new ServiceBuilder().apiKey(NAVER_CLIENT_ID).apiSecret(NAVER_CLIENT_SECRET).callback(serverUrl + NAVER_REDIRECT_URI).build(NaverOAuthApi20.instance());
OAuthRequest request = new OAuthRequest(Verb.GET, PROFILE_API_URL, oauthService);
oauthService.signRequest(oauthToken, request);
Response response = request.send();
return response.getBody();
}
private String generateRandomString() {
return UUID.randomUUID().toString();
}
private void setSession(HttpSession session, String state) {
session.setAttribute(SESSION_STATE, state);
}
private String getSession(HttpSession session) {
return (String) session.getAttribute(SESSION_STATE);
}
}
NaverOAuthApi20.java
@Component
public class NaverOAuthApi20 extends DefaultApi20 {
protected NaverOAuthApi20() {
}
private static class InstanceHolder {
private static final NaverOAuthApi20 INSTANCE = new NaverOAuthApi20();
}
public static NaverOAuthApi20 instance() {
return InstanceHolder.INSTANCE;
}
@Override
public String getAccessTokenEndpoint() {
return "https://nid.naver.com/oauth2.0/token";
}
@Override
protected String getAuthorizationBaseUrl() {
return "https://nid.naver.com/oauth2.0/authorize";
}
}
KakaoLoginBO.java
@Component
public class KakaoLoginBO {
// 카카오 로그인 정보
private final static String KAKAO_CLIENT_ID = "2112f2d3884100169e130e968f11c394";
private final static String KAKAO_CLIENT_SECRET = "시크리트 키";
private final static String KAKAO_REDIRECT_URI = "/login/kakaoOauth2ClientCallback.do";
private final static String SESSION_STATE = "kakao_oauth_state";
private final static String PROFILE_API_URL = "https://kapi.kakao.com/v2/user/me";
public String getAuthorizationUrl(HttpSession session, String serverUrl) {
String state = generateRandomString();
setSession(session, state);
OAuth20Service oauthService = new ServiceBuilder().apiKey(KAKAO_CLIENT_ID).apiSecret(KAKAO_CLIENT_SECRET).callback(serverUrl + KAKAO_REDIRECT_URI).state(state).build(KakaoOAuthApi20.instance());
return oauthService.getAuthorizationUrl();
}
public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state, String serverUrl) throws Exception {
String sessionState = getSession(session);
if (StringUtils.equals(sessionState, state)) {
OAuth20Service oauthService = new ServiceBuilder().apiKey(KAKAO_CLIENT_ID).apiSecret(KAKAO_CLIENT_SECRET).callback(serverUrl + KAKAO_REDIRECT_URI).state(state).build(KakaoOAuthApi20.instance());
OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
return accessToken;
}
return null;
}
public String getUserProfile(OAuth2AccessToken oauthToken, String serverUrl) throws Exception {
OAuth20Service oauthService = new ServiceBuilder().apiKey(KAKAO_CLIENT_ID).apiSecret(KAKAO_CLIENT_SECRET).callback(serverUrl + KAKAO_REDIRECT_URI).build(KakaoOAuthApi20.instance());
OAuthRequest request = new OAuthRequest(Verb.GET, PROFILE_API_URL, oauthService);
oauthService.signRequest(oauthToken, request);
Response response = request.send();
return response.getBody();
}
private String generateRandomString() {
return UUID.randomUUID().toString();
}
private void setSession(HttpSession session, String state) {
session.setAttribute(SESSION_STATE, state);
}
private String getSession(HttpSession session) {
return (String) session.getAttribute(SESSION_STATE);
}
}
KakaoOAuthApi20.java
@Component
public class KakaoOAuthApi20 extends DefaultApi20 {
protected KakaoOAuthApi20() {
}
private static class InstanceHolder {
private static final KakaoOAuthApi20 INSTANCE = new KakaoOAuthApi20();
}
public static KakaoOAuthApi20 instance() {
return InstanceHolder.INSTANCE;
}
@Override
public String getAccessTokenEndpoint() {
return "https://kauth.kakao.com/oauth/token";
}
@Override
protected String getAuthorizationBaseUrl() {
return "https://kauth.kakao.com/oauth/authorize";
}
}
pom.xml
<!-- OAuth2 -->
<dependency>
<groupId>com.github.scribejava</groupId>
<artifactId>scribejava-core</artifactId>
<version>2.8.1</version>
</dependency>
<!-- 제이슨 파싱 -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>