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; } }