<이전포스팅>

 

[Google 로그인] #2 FRONT 코드작성!

지난 포스팅에서 로그인 기능을 사용할 준비를 했다면 이제 직접 구현해보자! < 지난 포스팅! > [Google 로그인] #1 개발자 콘솔 가입! 1. 구글 개발자 콘솔에 로그인한다. console.developers.google.com/ Googl

genie-dev.tistory.com

 

Spring maven 환경에서 사용할

가이드 용도로 작업한거라 간단하게 기능만 짤라서 구현한 내용임! 

(시큐리티 적용 X)

 

순서는 이렇다~ 

1. 로그인페이지에서 구글 로그인 

2. 결과로 리턴받은 토큰을 서버로 전송 

3. 토큰을 이용해서 서버단에서 다시한번 사용자 정보 요청 

4. 프론트에서 넘어온 정보와 서버단에서 가져온 사용자 정보가 일치하는지 확인

5. 로그인 처리 or 내가 원하는 다른 작업을 진행하면 된다. 

 

pom.xml ( 필요한 기능들을 추가해준다. ) 

		<!--  새로 추가  -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-core</artifactId>
		    <version>2.12.1</version>
		</dependency>
		
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.12.1</version>
		</dependency>
		
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-annotations</artifactId>
		    <version>2.12.1</version>
		</dependency>
		
		<dependency>
		    <groupId>org.codehaus.jackson</groupId>
		    <artifactId>jackson-mapper-asl</artifactId>
		    <version>1.9.13</version>
		</dependency>
				
		
		<dependency>
		    <groupId>com.google.code.gson</groupId>
		    <artifactId>gson</artifactId>
		    <version>2.8.6</version>
		</dependency>
		
		<dependency>
	     <groupId>com.google.api-client</groupId>
	     <artifactId>google-api-client</artifactId>
	     <version>1.31.2</version>
	   </dependency>
	   
		<dependency>
		    <groupId>com.google.api-client</groupId>
		    <artifactId>google-api-client-jackson2</artifactId>
		    <version>1.20.0</version>
		</dependency>
	  

 

VO 생성 ( 내가 받을 내용을 작성하면 되는데 테스트용이므로 간단하게 작성했다.)

public class UserVO {
	private String email;
	private String token;
	
	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	} 
	
}

 

Controller 생성 ( 요청을 받을 컨트롤러를 작성한다)

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


import com.me.genie.service.GoogleService;
import com.me.genie.vo.UserVO;

import java.io.IOException;

@Controller
public class GoogleController {
	
	@Autowired GoogleService googleService;
	
	/**
	 * Authentication Code를 전달 받는 엔드포인트
     * 구글 개발자 사이트에서 리턴url로 적었을 경우만 가져옴
     * 백엔드에서 받는 방법도 보여주고자 작성함
	 * @throws IOException 
	 * @throws JsonProcessingException
	 * @throws JsonMappingException
	 **/
	@RequestMapping(value = "/google/auth")
	public String googleAuth(Model model, @RequestParam(value = "code") String authCode) throws IOException{
		// 구글이 리턴해준 코드가 들어온다.
		System.out.println("AUtho code : " + authCode);
		googleService.getToken(authCode);
		return "/home";
	}
	
	
	/**
	 * 프론트로부터 ajax 요청을 받는다.
	 * @param user
	 * @return
	 */
	@RequestMapping(value = "/google/userinfo", produces = "application/json")
	@ResponseBody 
	public Map<String, Object> revokeToken(UserVO user) {
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("res", googleService.tokenCheck(user));
		return result;

	}

}

 

Service ( 비즈니스 로직처리할 서비스 구현)

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;

import org.springframework.stereotype.Service;


import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;

import com.me.genie.service.GoogleService;
import com.me.genie.vo.UserVO;

@Service
public class GoogleService {
	final static String GOOGLE_AUTH_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
	final static String GOOGLE_TOKEN_BASE_URL = "https://oauth2.googleapis.com/token";
	final static String GOOGLE_REVOKE_TOKEN_BASE_URL = "https://oauth2.googleapis.com/revoke";
	final static String GOOGLE_GET_USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo?access_token=";
	final static String clientId = "나의 클라이언트키";
	final static String clientSecret = "비밀키";
	
	 private static final HttpTransport transport = new NetHttpTransport();
	 private static final JsonFactory jsonFactory = new JacksonFactory();
	
	 /**
	 * 백엔드단에서 토큰을 얻어온다.
	  * @param authCode
	  * @return access_Token
	  * @throws IOException
	  */
	public String getToken(String authCode) throws IOException {
		
		/**************************************
         * Access 토큰을 가져오기 위한 설정
         *************************************/
		URL url = new URL(GOOGLE_TOKEN_BASE_URL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setDoOutput(true);

        
        /**************************************
         * 파라메터 설정
         *************************************/
        StringBuffer sbf = new StringBuffer();
        sbf.append("code=");sbf.append(authCode);
        sbf.append("&client_id=");sbf.append(clientId);
        sbf.append("&client_secret=");sbf.append(clientSecret);
        sbf.append("&redirect_uri=");sbf.append("http://localhost:8080/google/auth");
        sbf.append("&grant_type=");sbf.append("authorization_code");
        String  parameterString = sbf.toString();
		
        
        /**************************************
         * 요청을 보내고 받을 스트림 생성
         *************************************/
		BufferedOutputStream bous = new BufferedOutputStream(conn.getOutputStream());
		bous.write(parameterString.getBytes()); 
		bous.flush(); 
		bous.close();
		
		BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		
		/**************************************
         * 받아온 데이터를 sb에 저장
         *************************************/
		StringBuilder sb = new StringBuilder(); 
		String line;
		while ((line = br.readLine()) != null) { 
			sb.append(line); 
		}
		
		/**************************************
         * 받아온 데이터에서 토큰 추출
         *************************************/
		String access_Token ="";
		if (conn.getResponseCode() == 200) { 
            JsonParser parser = new JsonParser();
            JsonElement element = parser.parse(sb.toString());
            access_Token = element.getAsJsonObject().get("access_token").getAsString();
            System.out.println("access_token : " + access_Token);
            
            if(access_Token!=null) {
                /***사용자 정보 요청 **/
            	userinfo(access_Token);
            }
		}
		return access_Token;
	}
	
	/**
	 * access_token 이용하여 사용자 정보를 가져온다.
	 * @param access_token
	 */
	public void userinfo(String access_token) {
		try { 
			
			/**************************************
	         * 사용자 정보를 가져오기 위한 셋팅
	         *************************************/
	        URL url = new URL(GOOGLE_GET_USER_INFO_URL+access_token);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("GET"); 
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			conn.setDoOutput(true);
			
			
			/**************************************
	         * 요청을 보내고 받을 스트림 생성
	         *************************************/
			BufferedOutputStream bous = new BufferedOutputStream(conn.getOutputStream());
			bous.flush(); 
			bous.close();
			
			BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			StringBuilder sb = new StringBuilder(); String line;
	  
			while ((line = br.readLine()) != null) { 
				sb.append(line); 
			}
			 
			/**************************************
	         * 받아온 데이터에서 출력
	         *************************************/
			if (conn.getResponseCode() == 200) { 
				System.out.println(sb.toString()); 
			}
			
		} catch (Exception e) {
			System.out.println("ERROR. . . PLEASE CHECK ... [GoogleService.userinfo]");
			e.printStackTrace();
		}
	}
	
	/**
	 * 사용자단에서 가져온 토큰값으로 
	 * 유효한사용자인지, 요청온 이메일은 맞는지 확인한다.
	 * @param user
	 * @return 유효여부
	 */
	public boolean tokenCheck(UserVO user) {
		boolean res = false;
		try {
			
			/**************************************
	         * 사용자 정보를 가져오기 위해 구글에서 
	         * 제공하는 설정 셋팅
	         *************************************/
			GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
			    .setAudience(Collections.singletonList(clientId))
			    .build();
		
			/**************************************
	         * 프론트 단에서 로그인 후 받아온 코드 설정 후 
	         * 사용자 정보를 가져온다.
	         *************************************/
			GoogleIdToken idToken = verifier.verify(user.getToken());
			
			// 사용자 정보가 존재하는지 확인
			if (idToken != null) {
			  // 사용자 정보가 담긴 데이터를 가져온다.
			  Payload payload = idToken.getPayload();
		
			  // 사용자 고유 ID 가져온다.
			  String userId = payload.getSubject();
			  System.out.println("User ID: " + userId);
		
			  // 그외 사용자 정보를 가져온다.
			  String email = payload.getEmail();
			  String name = (String) payload.get("name");
			  String pictureUrl = (String) payload.get("picture");
			  String locale = (String) payload.get("locale");
			  String familyName = (String) payload.get("family_name");
			  String givenName = (String) payload.get("given_name");
			  
			  /**************************************
	          * 프론트 단에서 로그인을 시도한 이메일과, 코드를 통해 가져온 정보가 일치하는지 확인한다. 
	          *************************************/
			  if(user.getEmail().equals(email)) { 
				  res = true; 
			  }
			  
			} else {
			  System.out.println("Invalid ID token.");
			}
			
		} catch (Exception e) {
			System.out.println("ERROR. . . PLEASE CHECK ... [GoogleService.userinfo]");
			e.printStackTrace();
		}
		return res;
	}
}

 

home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
	<script src="https://apis.google.com/js/platform.js" async defer></script>
	<meta name="google-signin-client_id" content="{발급받인 클라이언트 아이디}.apps.googleusercontent.com"></meta>
</head>
<body>

  
    <h1>로그인</h1>
    <hr>

    <form action="/user/login" method="post">
        <input type="text" name="username" placeholder="이메일 입력해주세요">
        <input type="password" name="password" placeholder="비밀번호">
        <button type="submit">로그인</button>
        
        <!-- 구글 로그인  -->
		<div class="g-signin2" data-onsuccess="onSignIn"></div>
		<!-- 구글 아웃 -->
		<a href="#" onclick="signOut()">구글 로그아웃</a>
        
        
  		
    </form>
	
    
    <script src="/resources/css/bower_components/jquery/dist/jquery.min.js"></script>
	<script src="/resources/css/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
	<script src="/resources/css/dist/js/adminlte.min.js"></script>
	<script src="/resources/js/utils/easy_ui/jquery.easyui.min.js"></script>
    <script>
	function onSignIn(googleUser) { 
		
    	var profile = googleUser.getBasicProfile(); 
		/*
    	console.log('ID: ' + profile.getId()); // 이건 백엔드로 보내면 안됨! 보안의 위험이 있음. 
    	console.log('Name: ' + profile.getName()); 
    	console.log('Image URL: ' + profile.getImageUrl()); 
    	console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.
		console.log(googleUser.getAuthResponse().id_token); // 서버에 던질땐 보안을 위해 이값으로 던져준다.
		*/
		
		var email = profile.getEmail();
		var token = googleUser.getAuthResponse().id_token;
    	$.ajax({
    	    url:'/google/userinfo', 
    	    type:'post', 
    	    data:{'token': token, 'email': email}, 
    	    success: function(data) {
    	        if(data.res) { 
	    	        alert("OK");
       	        } else {
       	        	alert("비정상로그인 접근입니다.");
           	    }
    	    },
    	    error: function(err) {
    	        //서버로부터 응답이 정상적으로 처리되지 못햇을 때 실행
    	    }
    	});
    }
	 
	function signOut() { 
    	var auth2 = gapi.auth2.getAuthInstance(); 
    	auth2.signOut().then(function () {
        	 self.location="로그아웃할 url"; 
        }); 
   	 } 
  	</script>

   
</body>
</html>


 

+ Recent posts