<이전포스팅>

 

[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>


 

실습에 앞서 간단한 개념정리를 해본다~~ 

 

@ Ajax 란? 

JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 데이터를 주고받는 기술이다.
즉, 쉽게 말하자면 자바스크립트를 통해서 서버에 데이터를 요청하는 것이다.

 

@ 비동기 방식이란?

프로그램 언어마다 다를 수 있겠지만 기본적으로 프로그램은 절차적 즉 순서대로 동작한다. 
그래서 하나의 동작이 끝나면 다음 동작이 실행 또 그 다음 다음 ... 이렇게 쭉 가는건데 
매번 순서대로 하다보면 효율이 낮을때가 있다. 

예를 들면,,  


기본구조가 
자판기에서 커피를 누르고 잔돈을 받으려면 커피가 다 나오고 커피를 꺼내야 잔돈을 받을 수 있는 구조라면 


비동기는 

자판기에서 커피를 누르고 커피가 나오는 동안 잔돈 버튼을 눌러서 잔돈을 받을 수 있게 해준다



어떤 일(커피추출)을 진행하는 동안 이라는 다른일(잔돈반환)을 수행할 수 있게되는것! 


이렇게 하나의 로직이 수행된 후 다음 로직이 실행되는 절차적 수행을 피할 수 있는 방법이다. 

또 비동기 방식은 웹페이지를 리로드하지 않고 데이터를 불러올 수 있다는 장점이 있다. 



사람이 한다면 별거 아니지만 컴터에게 시키려면 이런 절차가 필요하다 ~~ 

 

 

다음 포스팅 ~~! 

 

SpringBoot Ajax 사용하기!

Spring Framework 에서 사용하는 Ajax 샘플 코드를 정리했다. 흐름은 이렇다~~~~ 1. html에서 Send 버튼을 클릭! 2. 버튼 클릭 이번트 감지 function 실행 3. 보낼 데이터 준비 후 ajax url(/send) 로 전송~ 4. @P..

genie-dev.tistory.com

 

'JAVA 강의 ( 초급)' 카테고리의 다른 글

SpringBoot Ajax 사용하기!  (0) 2021.02.18
JSON 에 대해서 알아봅시다.  (0) 2021.02.17
JAVA 기초 3강  (0) 2020.10.04
자바 2강!  (0) 2020.09.26

 

이전 포스팅 

 

AJAX 기초 수업 개념 정리!!

실습에 앞서 간단한 개념정리를 해본다~~ @ Ajax 란? JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 데이터를 주고받는 기술이다. 즉, 쉽게 말하자면 자바스크립트를 통해서 서

genie-dev.tistory.com

 

Spring Framework 에서 사용하는 Ajax 샘플 코드를 정리했다. 

 

흐름은 이렇다~~~~ 

1. html에서 Send 버튼을 클릭! 

2. 버튼 클릭 이번트 감지 function 실행

3. 보낼 데이터 준비 후  ajax url(/send)  로 전송~ 

4. @PostMapping(value = "/send") url 컨트롤러 매핑 

5. Controller 안에서 필요한 작업 수행~  

6. $.ajax 안의 success로 콜백 ~! 

7. error 발생 시 $.ajax 안 error 실행 

 

샘플코드로 확인하기! 

 

test.html 

<form id="testForm" method="post">
  이름 : <input type="text" name="name" id="name" value="genie"> <br/>
  나이 : <input type="text" name="age"  id="age" value="29"><br/>
  사는곳 : <input type="text" name="city" id="city" value="seoul"><br/>
  <button id="sendBtn">Send</button>
</form>

 

JS

 $(function() { 
   $("#sendBtn").click(function(e){
     e.preventDefault();


    /***********************
    * 기본 방식
    ************************/
    var form = {
    	name: "genie",
    	age: 29
    }

    $.ajax({
    	url: "/test/form",
    	type: "POST",
      	data: form,
      	success: function(data){
     	 	console.log(data);
      	},
      	error: function(){
          alert("error .. ");
        }
    });


    /***********************
    * form 으로 보내기 
    ************************/
    $.ajax({
      url: "/test/form",
      type: "POST",
      data: $("#testForm").serialize(),
      success: function(data){
     	 console.log(data);
      },
      error: function(){
      	alert("error .. ");
      }
    });





    /***********************
    * stringify 으로 보내기 
    ************************/
    var form2 = {
      name: "genie",
      age: 29
    }

    // 형태 변환 확인해보기 
    console.log(JSON.stringify(form2));  // print : {"name":"genie","age":29}

    console.log(JSON.stringify($('#testForm').serializeArray())); 
    // print : [{"name":"name","value":"genie"},{"name":"age","value":"29"},{"name":"city","value":"seoul"}]

    $.ajax({
      url: "/send",
      type: "POST",
      data: JSON.stringify(form),
      dataType: "json",
      contentType: "application/json; charset=utf-8;",
      success: function(data){
     	 console.log(data)
      },
      error: function(){
     	 alert("error .. ");
      }
    });

  });

});

 

VO

public class UserVO {
	private String name;
	private int age;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

 

Controller

@Controller
public class HomeController {
	
	
    @PostMapping("/test/form")
    @ResponseBody
    public String test1(UserVO vo) {
    	
    	/**************************
    	 * vo 에 담긴 param 값 꺼내보기 
    	 **************************/
    	System.out.println("======================");
    	System.out.println("이름 : " + vo.getName());
    	System.out.println("나이 : " + vo.getAge());
    	System.out.println("======================");
    	
    	return "OK";
    }
    
    
    
    @PostMapping(value = "/send")
    public @ResponseBody Map<String, Object> send(@RequestBody Map<String, Object> param){
    	
    	/**************************
    	 * map에 담긴 param 값 꺼내보기 
    	 **************************/
    	System.out.println("======================");
    	System.out.println(param);
    	System.out.println("이름 : " + param.get("name"));
    	System.out.println("나이 : " + param.get("age"));
    	System.out.println("======================");

    	
    	/**************************
    	 * map에서 가져온값 VO에 담아보기 
    	 **************************/
    	UserVO user = new UserVO();
    	user.setName((String) param.get("name"));
    	user.setAge((int) param.get("age"));
    	
    	
    	/**************************
    	 * 객체를 리턴해보기 
    	 **************************/
    	Map<String, Object> res = new HashMap<>();
    	res.put("vo", user);
    	res.put("msg", "OK");
    	
    	
    	return res;
	}
    
}

 

 

끝!! 굳!!!!  

'JAVA 강의 ( 초급)' 카테고리의 다른 글

AJAX 기초 수업 개념 정리!!  (0) 2021.02.18
JSON 에 대해서 알아봅시다.  (0) 2021.02.17
JAVA 기초 3강  (0) 2020.10.04
자바 2강!  (0) 2020.09.26

 

* 에러발생 

html 파일에서 ajax로 

통신이 이루어지는지 잘 보이기 위한 테스트 페이지를 구현하기 위해

url이 다른 서버로 요청을 보냈다. 

 

Access to XMLHttpRequest at 'file:///D:/apache/index.html' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, brave, https.

 

* 원인

같은 경로의 자원을 요청하는데 에러 메세지엔 보시다시피 origin, 즉 출처가 null 로 넘어온 script에 대한 접근이 CORS 정책에 따라 제한되었다고 나와있습니다. 

 

D:/apache/index.htm 에서 ajax로 c:/경로/js/test.js에 리소스를 요청한 건 동일 경로의 리소스를 요청한 것이 아니고
D:/apache/index.htm 에서 null/js/test.js로 리소스를 요청한 것이 되어 CORS에러가 발생한 것입니다.

 

 

 

웹 서버에 올려 프로토콜 호스트 포트를 같게 만들면 CORS 에러가 해결되는데,

 

같게 할 수 없는 상황에선 특정 플러그인을 사용하거나 jsonp 방식을 사용한다. 

 

+ Recent posts