티스토리 뷰

Programming/Java

Spring MVC - 네이버 로그인 처리

파란크리스마스 2017. 4. 3. 16:05
728x90

Spring MVC - 네이버 로그인 처리

네이버 로그인 API를 이용해서 네이버 계정으로 로그인 처리를 Spring 프레임웍에서 사용 할 수 있도록 간단하게 NaverLoginController를 작성해보았습니다. 임시로 로그인 결과값을 가지고 와서 tbluser2 테이블의 abcd 계정의 1111 암호를 사용하는 사용자로 로그인 되도록 고정했습니다. (authentication-provider 사용자가 제어 하는 부분은 따로 찾아서 적용필요)

bluexmas-security.xml

alias의 authenticationManager 이름은 NaverLoginController 에서 속성으로 사용

	<security:authentication-manager alias="authenticationManager">
		<security:authentication-provider>

			<security:jdbc-user-service
				data-source-ref="dataSource"
				users-by-username-query="select user_id username, pass password, 1 as enabled from tbluser2 where user_id = ?"
				authorities-by-username-query="select user_id username, 'ROLE_USER' authority from tbluser2 where user_id = ?" />

		</security:authentication-provider>
	</security:authentication-manager>

NaverLogInController.java

package com.bluexmas.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SecureRandom;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

@Controller
public class NaverLogInController {
	
	private String naverClientId = "네이버API_Client_ID";
	
	private String naverClientSecret = "네이버API_Client_Secret";
	
	private String state = "";
	
	@Autowired
	@Qualifier("authenticationManager")
	protected AuthenticationManager authenticationManager;
	
	/**
	 * 네이버 로그인 요청
	 * 
	 * @param request
	 * @param uri
	 * @return
	 */
	@RequestMapping(value = "/naverLogin.do")
	public String makeNaverRequestStatement(
			HttpServletRequest request,
			@RequestParam(value = "uri", required = false) String uri) 
	{
		HashMap<String, String> naverUrl = this.makeRequestStatement(request, naverClientId);
		this.state = naverUrl.get("state");
		String returnUrl = request.getHeader("referer");
		
		return "redirect:" + naverUrl.get("url");
	}
	
	/**
	 * 네이버 로그인 요청에 대한 응답 처리
	 * 
	 * @param request
	 * @param response
	 * @param state
	 * @param code
	 * @param error
	 * @param error_description
	 * @return
	 */
	@RequestMapping(value = "/naverRequestKey.do")
	public String getNaverRequestKey(
			HttpServletRequest request,
			HttpServletResponse response,
			@RequestParam("state") String state, 
			@RequestParam(value = "code", required = false) String code, 
			@RequestParam(value = "error", required = false) String error, 
			@RequestParam(value = "error_description", required = false) String error_description) 
	{
		HttpSession session = request.getSession();
		
		System.out.print("state = " + this.state + "="+  state + "/code = " + code + "/error = " + error + "/error_description = " + error_description);
		
		if (state.equals(this.state)) {
			session.setAttribute("isLoged", true);
		} else {
			session.setAttribute("isLoged", false);
			//System.out.println("state key값 불일치. 인증에러");
			return "redirect:/index.html";
		}
		
		// 네이버에 token_type, access_token 요청
		HashMap<String, String> result1 = this.getRequestKey(request, state, code, naverClientId, naverClientSecret);
		
		//
		String access_token = result1.get("access_token");
		String refresh_token = result1.get("refresh_token");
		String token_type = result1.get("token_type");
		
		System.out.println("access_token = " + access_token + "," + refresh_token + "," + token_type);

		// 사용자 정보 조회 요청
		HashMap<String, Object> result2 = this.requestUserInfo(token_type, access_token);
		System.out.println("result=" + result2);

		String email = (String)result2.get("email");
		
		if (email==null) {
			System.out.println("email = " + email);
			session.setAttribute("isLoged", false);
			return "redirect:/index.html";
		}
		
		String user_id = (String)result2.get("email");
		
		// 수동 로그인(spring security manual login) 처리
		try {
			// 
			UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("abcd", "1111");
			
			// Authenticate the user
			Authentication authentication = authenticationManager.authenticate(authRequest);
			SecurityContext securityContext = SecurityContextHolder.getContext();
			securityContext.setAuthentication(authentication);
		} catch (Exception e) {
			e.printStackTrace();
			SecurityContextHolder.getContext().setAuthentication(null);
		}
		
		//return "redirect:" + returnUrl;
		return "redirect:/index.html";
	}
	
	/**
	 * 호스트이름 반환
	 * 
	 * @param request
	 * @return
	 */
	public static String getHostname(HttpServletRequest request) {
		String port_str = (request.getServerPort()==80 ? "" : ":" + request.getServerPort());
		return "http://" + request.getServerName() + port_str + request.getContextPath();
	}
	
	/**
	 * state 값 생성
	 * 
	 * @return
	 */
	public String generateState() {
		SecureRandom random = new SecureRandom();
		return new BigInteger(130, random).toString(32);
	}
	
	/**
	 * 네이버 로그인 요청 (Redirect URL 생성)
	 * 
	 * 1. redirect 생성 - 네이버 로그인 요청 완료 후 응답 받는 url 생성
	 * 2. state 생성 - Ramdom 함수로 생성
	 * 3. 요청 URL 생성 반환
	 * 
	 * @param request
	 * @param clientId
	 * @return
	 */
	private HashMap<String, String> makeRequestStatement(HttpServletRequest request, String clientId) {
		//String redirectUri = "http://127.0.0.1:8080/naverRequestKey.cmx";
		String redirectUri = getHostname(request) + "/naverRequestKey.do";
		String state = generateState();
		String url = "https://nid.naver.com/oauth2.0/authorize?" + "response_type=code&client_id=" + clientId + "&redirect_uri=" + redirectUri + "&state=" + state;
		
		HashMap<String, String> naverUrl = new HashMap<String, String>();
		naverUrl.put("state", state);
		naverUrl.put("url", url);
		return naverUrl;
	}
	
	/**
	 * 네이버에 token_type, access_token 요청(로그인 성공 이후)
	 * 
	 * @param request
	 * @param state
	 * @param code
	 * @param clientId
	 * @param naverClientSecret
	 * @return
	 */
	private HashMap<String, String> getRequestKey(HttpServletRequest request, String state, String code, String clientId, String naverClientSecret) {
		String naverUrl = "https://nid.naver.com/oauth2.0/token?client_id=" + clientId + "&client_secret=" + naverClientSecret + "&grant_type=authorization_code" + "&state=" + state + "&code=" + code;
		String naverJsonKey = "";// null로 하니까 null이 json에 포함이 됨.
		HashMap<String, String> result = new HashMap<String, String>();

		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(naverUrl).openConnection();
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
			String input;
			while ((input = reader.readLine()) != null) {
				naverJsonKey += input;
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			JSONParser parser = new JSONParser();
			JSONObject jsonObject = (JSONObject) parser.parse(naverJsonKey);
			
			result.put("access_token", (String) jsonObject.get("access_token"));
			result.put("refresh_token", (String) jsonObject.get("refresh_token"));
			result.put("token_type", (String) jsonObject.get("token_type"));
			
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 네이버에 로그인 사용자의 정보 요청
	 * 
	 * @param token_type
	 * @param access_token
	 * @return
	 */
	public HashMap<String, Object> requestUserInfo(String token_type, String access_token) {
		
		String url = "https://apis.naver.com/nidlogin/nid/getUserProfile.xml";
		String naverResult = "";
		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
			conn.setRequestMethod("GET");
			conn.setRequestProperty("Authorization", token_type + " " + access_token);
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
			String input;
			while ((input = reader.readLine()) != null) {
				naverResult += input;
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		HashMap<String, Object> result = new HashMap<String, Object>();

		try {
			InputSource is = new InputSource(new StringReader(naverResult));
			Document xml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
			XPath xpath = XPathFactory.newInstance().newXPath();
			Node email = (Node) xpath.evaluate("//data/response/email", xml, XPathConstants.NODE);
			Node nickname = (Node) xpath.evaluate("//data/response/nickname", xml, XPathConstants.NODE);
			Node profile_image = (Node) xpath.evaluate("//data/response/profile_image", xml, XPathConstants.NODE);
			Node age = (Node) xpath.evaluate("//data/response/age", xml, XPathConstants.NODE);
			Node gender = (Node) xpath.evaluate("//data/response/gender", xml, XPathConstants.NODE);
			Node id = (Node) xpath.evaluate("//data/response/id", xml, XPathConstants.NODE);
			Node birthday = (Node) xpath.evaluate("//data/response/birthday", xml, XPathConstants.NODE);
			if (email!=null) result.put("email", email.getTextContent());
			if (nickname!=null) result.put("nickname", nickname.getTextContent());
			if (profile_image!=null) result.put("profile_image", profile_image.getTextContent());
			if (age!=null) result.put("age", age.getTextContent());
			if (gender!=null) result.put("gender", gender.getTextContent());
			if (id!=null) result.put("id", id.getTextContent());
			if (birthday!=null) result.put("birthday", birthday.getTextContent());

		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

}


댓글
300x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함