네이버 로그인 api는 로그인 하는것만 자세히 나와있고
로그아웃을 알아서 하라한다..
정보도 너무 안나오고 그래서 내가 직접쓰려고 한다.
먼저 login을 하면 naver 회원 정보에 저장되어있는 유저의 정보들이 넘어온다.
그걸 진짜 가입이 된 것 처럼 db에 insert 시킨다.
그리고 탈퇴를 하면 naver 접속 토큰을 삭제시켜서 naver login api의 연동을 해제시키는 것을 하려고 한다.
링크텍스트
탈퇴처리가 완료되면 여기서 LibraryLogin이 안떠야한다
계쏙 확인하면서 진행하기!
package org.book.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.book.domain.UserDTO;
import org.book.service.UserService;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.login.api.NaverLoginBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.github.scribejava.core.model.OAuth2AccessToken;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
//@AllArgsConstructor
@Controller
@Log4j
public class LoginController {
/* NaverLoginBO */
private NaverLoginBO naverLoginBO;
private String apiResult = null;
//UserDTO
@Setter(onMethod_=@Autowired)
private UserService service;
@Autowired private void setNaverLoginBO(NaverLoginBO naverLoginBO) {
this.naverLoginBO = naverLoginBO; }
// 로그인 첫 화면 요청 메소드
@RequestMapping(value = "/login", method = { RequestMethod.GET, RequestMethod.POST })
public String login(Model model, HttpSession session) {
/*
* 네이버아이디로 인증 URL을 생성하기 위하여 naverLoginBO클래스의 getAuthorizationUrl메소드 호출
*/
String naverAuthUrl = naverLoginBO.getAuthorizationUrl(session);
// https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=sE***************&
// redirect_uri=http%3A%2F%2F211.63.89.90%3A8090%2Flogin_project%2Fcallback&state=e68c269c-5ba9-4c31-85da-54c16c658125
System.out.println("네이버:" + naverAuthUrl);
// 네이버
model.addAttribute("url", naverAuthUrl);
return "login";
}
// 네이버 로그인 성공시 callback호출 메소드
@RequestMapping(value = "/callback", method = { RequestMethod.GET, RequestMethod.POST })
public String callback(@RequestParam String code, @RequestParam String state, HttpSession session, Model model)
throws IOException, ParseException {
System.out.println("여기는 callback state 호출해보겟다"+state);
OAuth2AccessToken oauthToken;
oauthToken = naverLoginBO.getAccessToken(session, code, state); // return accessTocken
log.info("state 호출해봄 from callback " +state);
// 1. 로그인 사용자 정보를 읽어온다.
apiResult = naverLoginBO.getUserProfile(oauthToken); // String형식의 json데이터
System.out.println("혹시 이거???" + apiResult);
log.info("oauthToken이 이거임!!!!!!!!!"+oauthToken);
/**
* apiResult json 구조 {"resultcode":"00", "message":"success",
* "response":{"id":"33666449","nickname":"shinn****","age":"20-29",
* "gender":"M","email":"sh@naver.com","name":"\uc2e0\ubc94\ud638"}}
**/
// 2. String형식인 apiResult를 json형태로 바꿈
JSONParser parser = new JSONParser();
Object obj = parser.parse(apiResult);
System.out.println("obj는????" + obj);
JSONObject jsonObj = (JSONObject) obj;
// 3. 데이터 파싱
// Top레벨 단계 _response 파싱
JSONObject response_obj = (JSONObject) jsonObj.get("response");
String access_token = oauthToken.getAccessToken(); //토큰
log.info("getAccessTocken 의 타입이 뭐냐???????????????"+oauthToken.getAccessToken().getClass().getName());
log.info("access_token 의 타입이 뭐냐???????????????"+access_token.getClass().getName());
log.info("access_token값?????????"+access_token); //따옴표 안붙고 잘나옴
System.out.println("response_obj??????????" + response_obj);
// response의 nickname값 파싱
String id = (String) response_obj.get("id");
String name = (String) response_obj.get("name");
String email = (String) response_obj.get("email");
String age = (String) response_obj.get("age");
String mobile = (String) response_obj.get("mobile");
String profile_image = (String) response_obj.get("profile_image");
String gender = (String) response_obj.get("gender");
Integer birthyear = Integer.valueOf((String)response_obj.get("birthyear")) ;
String birthday = (String) response_obj.get("birthday");
// 4.파싱 user네임 세션으로 저장
session.setAttribute("sessionId", name);
// 세션 생성
session.setAttribute("result", response_obj);
String str_result = access_token.replaceAll("\\\"","");
//String encodeResult = URLEncoder.encode(access_token, "UTF-8");
session.setAttribute("access_token", str_result);
//log.info("encodeResult값?????????"+encodeResult); //따옴표 없이 잘나옴
UserDTO dto = new UserDTO(id,name,email,age,mobile,profile_image,gender,birthyear, birthday);
System.out.println("dto 니놈은뭐니???????"+dto.getName());
System.out.println("==============dto 타입==================");
System.out.println( dto instanceof UserDTO );
System.out.println("================================");
//userId가 db에 있는지 만들기
if(!(service.insertCheck(id))) {
service.signup(dto);
}
return "login";
}
// 로그아웃
@RequestMapping(value = "/logout", method = { RequestMethod.GET, RequestMethod.POST })
public String logout(HttpSession session) throws IOException {
System.out.println("여기는 logout");
session.invalidate();
return "redirect:Book/home";
}
@GetMapping("/remove") //token = access_token임
public String remove(@RequestParam String token,HttpSession session, HttpServletRequest request, Model model ) {
log.info("토큰 삭제중...");
String apiUrl = "https://nid.naver.com/oauth2.0/token?grant_type=delete&client_id="+NaverLoginBO.CLIENT_ID+
"&client_secret="+NaverLoginBO.CLIENT_SECRET+"&access_token="+token.replaceAll("'", "")+"&service_provider=NAVER";
log.info("apiUrl====="+apiUrl);
try {
String res = requestToServer(apiUrl);
model.addAttribute("res", res); //결과값 찍어주는용
session.invalidate();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "/Book/home";
}
private String requestToServer(String apiURL) throws IOException {
return requestToServer(apiURL, null);
}
private String requestToServer(String apiURL, String headerStr) throws IOException {
URL url = new URL(apiURL);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setRequestMethod("GET");
System.out.println("header Str: " + headerStr);
if(headerStr != null && !headerStr.equals("") ) {
con.setRequestProperty("Authorization", headerStr);
}
int responseCode = con.getResponseCode();
BufferedReader br;
System.out.println("responseCode="+responseCode);
if(responseCode == 200) { // 정상 호출
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else { // 에러 발생
br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
}
String inputLine;
StringBuffer res = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
res.append(inputLine);
}
br.close();
if(responseCode==200) {
String new_res=res.toString().replaceAll("'", "");
return new_res;
} else {
return null;
}
}
@GetMapping("/update")
public String update(@RequestParam String id, Model model) {
log.info("updateForm으로 이동...");
model.addAttribute("list",service.getUserList(id));
return "/Book/mypageUpdateFrm";
}
@PostMapping("/update")
public String updateProc(UserDTO dto) {
log.info("post update/............"+dto);
service.update(dto);
return "redirect:/update";
}
}
설명을 간단히 하자면 로그인이 성공되면 /callback 으로 들어오니까
이때 db에 넣고
//userId가 db에 있는지 만들기
if(!(service.insertCheck(id))) {
service.signup(dto);
}
이때 db에 두번 넣어주면 안되니까 있으면 안넣고, 없어야지만 넣는다.
그리고
remove 부분을 보라
@GetMapping("/remove") //token = access_token임
public String remove(@RequestParam String token,HttpSession session, HttpServletRequest request, Model model ) {
log.info("토큰 삭제중...");
String apiUrl = "https://nid.naver.com/oauth2.0/token?grant_type=delete&client_id="+NaverLoginBO.CLIENT_ID+
"&client_secret="+NaverLoginBO.CLIENT_SECRET+"&access_token="+token.replaceAll("'", "")+"&service_provider=NAVER";
log.info("apiUrl====="+apiUrl);
try {
String res = requestToServer(apiUrl);
model.addAttribute("res", res); //결과값 찍어주는용
session.invalidate();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "/Book/home";
}
apiUrl이 토큰 삭제하는 url인데 이거를 입력하면 삭제가 바로 되어버리는 매우 쉬운 기능이다
근데 나는 안됐음..
이유는
계속 access_token에 작은따옴표가 들어갔음..
심지어 '엑세스토큰'으로 들어가서
삭제가안됨
그래서 replace를 이용해서 따옴표 없애주기로 결정
String new_res=res.toString().replaceAll("'", "");
return new_res;
이 코드를 넣어서 작은따옴표를 없애주었다..
잘됨.....
끝
package org.login.api;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
import lombok.extern.log4j.Log4j;
@Log4j
public class NaverLoginBO {
/* 인증 요청문을 구성하는 파라미터 */
// client_id: 애플리케이션 등록 후 발급받은 클라이언트 아이디
// response_type: 인증 과정에 대한 구분값. code로
// 값이 고정돼 있습니다.
// redirect_uri: 네이버 로그인 인증의 결과를 전달받을 콜백 URL(URL 인코딩). 애플리케이션을
// 등록할 때 Callback URL에 설정한 정보입니다.
// state: 애플리케이션이 생성한 상태 토큰
public final static String CLIENT_ID = "너님 아이디";
public final static String CLIENT_SECRET = "너님비번";
public final static String REDIRECT_URI = "http://localhost:8080/callback";
public final static String SESSION_STATE = "oauth_state";
/* 프로필 조회 API URL */
private final static String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";
/* 네이버 아이디로 인증 URL 생성 Method */
public String getAuthorizationUrl(HttpSession session) {
/* 세션 유효성 검증을 위하여 난수를 생성 */
String state = generateRandomString();
/* 생성한 난수 값을 session에 저장 */
setSession(session,state);
/* Scribe에서 제공하는 인증 URL 생성 기능을 이용하여 네아로 인증 URL 생성 */
OAuth20Service oauthService = new ServiceBuilder()
.apiKey(CLIENT_ID)
.apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI)
.state(state) //앞서 생성한 난수값을 인증 URL생성시 사용함
.build(NaverLoginApi.instance());
return oauthService.getAuthorizationUrl();
}
/* 네이버아이디로 Callback 처리 및 AccessToken 획득 Method */
public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state) throws IOException{
/* Callback으로 전달받은 세선검증용 난수값과 세션에 저장되어있는 값이 일치하는지 확인 */
String sessionState = getSession(session);
log.info("sessionState 있나?"+sessionState);
log.info("state 있나?"+state);
if(StringUtils.pathEquals(sessionState, state)){
OAuth20Service oauthService = new ServiceBuilder()
.apiKey(CLIENT_ID)
.apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI)
.state(state)
.build(NaverLoginApi.instance());
/* Scribe에서 제공하는 AccessToken 획득 기능으로 네아로 Access Token을 획득 */
OAuth2AccessToken accessToken =oauthService.getAccessToken(code);
return accessToken;
}
return null;
}
/* 세션 유효성 검증을 위한 난수 생성기 */
private String generateRandomString() {
return UUID.randomUUID().toString();
}
/* http session에 데이터 저장 */
private void setSession(HttpSession session,String state){
session.setAttribute(SESSION_STATE, state);
}
/* http session에서 데이터 가져오기 */
private String getSession(HttpSession session){
return (String) session.getAttribute(SESSION_STATE);
}
/* Access Token을 이용하여 네이버 사용자 프로필 API를 호출 */
public String getUserProfile(OAuth2AccessToken oauthToken) throws IOException{
OAuth20Service oauthService =new ServiceBuilder()
.apiKey(CLIENT_ID)
.apiSecret(CLIENT_SECRET)
.callback(REDIRECT_URI).build(NaverLoginApi.instance());
OAuthRequest request = new OAuthRequest(Verb.GET, PROFILE_API_URL, oauthService);
oauthService.signRequest(oauthToken, request);
Response response = request.send();
return response.getBody();
}
}
여기서 public static 어쩌고로 썼는데 원래는 private이었음
잘 몰라서 그냥 public으로 하고 controller에서 꺼내썼는데
좋은방식은 아닌거같다..
누군가 좀 더 좋은 방법을 알려주세요...