Spring Naver Login api 연동 해제, 접속토큰 삭제하기

뿌이·2022년 1월 19일
1

api연동

목록 보기
3/3

네이버 로그인 api는 로그인 하는것만 자세히 나와있고
로그아웃을 알아서 하라한다..
정보도 너무 안나오고 그래서 내가 직접쓰려고 한다.

개요

먼저 login을 하면 naver 회원 정보에 저장되어있는 유저의 정보들이 넘어온다.
그걸 진짜 가입이 된 것 처럼 db에 insert 시킨다.
그리고 탈퇴를 하면 naver 접속 토큰을 삭제시켜서 naver login api의 연동을 해제시키는 것을 하려고 한다.

확인하기

링크텍스트

탈퇴처리가 완료되면 여기서 LibraryLogin이 안떠야한다
계쏙 확인하면서 진행하기!

LoginController.java 전체코드

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에서 꺼내썼는데
좋은방식은 아닌거같다..
누군가 좀 더 좋은 방법을 알려주세요...

profile
기록이 쌓이면 지식이 된다.

0개의 댓글