API에 호출에 필요한 필수 항목

  • Client ID : API를 주관하는 애플리케이션 고유 키
  • Client Secret : 보안을 강화하기 위해 추가로 확인하는 애플리케이션 코드
  • Redirect URI : 로그인 성공 후 Return 데이터를 받을 서버 주소 (API 애플리케이션에 등록되어 있어야 함)
  • scope : 접근하고자 하는 사용자 리소스 정의

로그인 페이지 호출

URL : https://accounts.google.com/o/oauth2/v2/auth
메서드 : GET or POST

@RequestMapping("/google_login")
public String google_login(HttpServletRequest request) {
    String client_id = [CLIENT ID];
    String redirect_uri = [REDIRECT URI];
    String state = [STATE];   // 필수 X
    String login_url = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code"
            + "&client_id=" + client_id
            + "&redirect_uri=" + redirect_uri
            + "&state=" + state   // 필수 X
            + "&scope=email profile";

    request.getSession().setAttribute("state", state);

    return "redirect:" + login_url;
}
  • response_type : ‘code’ 로 고정. 필수 O
  • state : CSRF 공격 방지를 위한 임의의 문자열
  • scope : 사용자 정보를 얻고 싶다면 ‘email profile’로 지정

Access_token 가져오기

구글 로그인에 성공하게 되면 redirect uri 주소로 구글이 code 값을 보내주는데 이 code 값을 가지고 구글에 access_token 을 요청해야 한다.

URL : https://oauth2.googleapis.com/token
메서드 : GET or POST

@RequestMapping("/google_redirect")
public String google_redirect(HttpServletRequest request) {
	// 구글에서 전달해준 code, state 값 가져오기
    String code = request.getParameter("code");
    String state = request.getParameter("state");

	// 세션에 저장해둔 state값 가져오기
    String session_state = String.valueOf(request.getSession().getAttribute("state"));
	
	// CSRF 공격 방지를 위해 state 값 비교
    if (!state.equals(session_state)) {
        System.out.println("세션 불일치");
        request.getSession().removeAttribute("state");
        return "/sns/sns_error";
    }

    String tokenURL = "https://oauth2.googleapis.com/token";
    String client_id = [CLIENT ID];
    String client_secret = [CLIENT SECRET];
    String redirect_uri = [REDIRECT URI];

    // body data 생성
    MultiValueMap<String, String> parameter = new LinkedMultiValueMap<>();
    parameter.add("grant_type", "authorization_code");
    parameter.add("client_id", client_id);
    parameter.add("client_secret", client_secret);
    parameter.add("code", code);
    parameter.add("redirect_uri", redirect_uri);

    // request header 설정
    HttpHeaders headers = new HttpHeaders();
    // Content-type을 application/x-www-form-urlencoded 로 설정
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    // header 와 body로 Request 생성
    HttpEntity<?> entity = new HttpEntity<>(parameter, headers);

    try {
        RestTemplate restTemplate = new RestTemplate();
        // 응답 데이터(json)를 Map 으로 받을 수 있도록 관련 메시지 컨버터 추가
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        // Post 방식으로 Http 요청
        // 응답 데이터 형식은 Hashmap 으로 지정
        ResponseEntity<HashMap> result = restTemplate.postForEntity(tokenURL, entity, HashMap.class);
        Map<String, String> resMap = result.getBody();

		// 응답 데이터 확인
        System.out.println(resMap);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return "/sns/sns_result";
}
  • 바디 데이터 중 grant_type 은 ‘authorization_code’ 로 고정
  • MappingJackson2HttpMessageConverter 를 사용하기 위해서는 com.fasterxml.jackson.core:jackson-databind 의존성 필요

사용자 정보 가져오기

URL : https://www.googleapis.com/userinfo/v2/me
메서드 : GET

...

// 리턴받은 access_token 가져오기
String access_token = resMap.get("access_token");

String userInfoURL = "https://www.googleapis.com/userinfo/v2/me";
// Header에 access_token 삽입
headers.set("Authorization", "Bearer "+access_token);

// Request entity 생성
HttpEntity<?> userInfoEntity = new HttpEntity<>(headers);

// GET 방식으로 Http 요청
// 응답 데이터 형식은 Hashmap 으로 지정
ResponseEntity<HashMap> userResult = restTemplate.exchange(userInfoURL, HttpMethod.GET, userInfoEntity, HashMap.class);
Map<String, String> userResultMap = userResult.getBody();

//응답 데이터 확인
System.out.println(userResultMap);

...

예제 소스

  1. 구글 로그인 버튼 화면
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="<c:url value="/google_login" />">
    <img src="/images/sns_login/google_sns_icon.png">
</a>
</body>
</html>
  1. 구글 로그인 페이지 호출
@RequestMapping("/google_login")
public String google_login(HttpServletRequest request) {
    String client_id = [CLIENT ID];
    String redirect_uri = {contextPath}"/google_redirect";
    String state = RandomStringUtils.randomAlphabetic(10);   // 랜덤 문자열 생성
    String login_url = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code"
            + "&client_id=" + client_id
            + "&redirect_uri=" + redirect_uri
            + "&state=" + state
            + "&scope=email profile";

    request.getSession().setAttribute("state", state);

    return "redirect:" + login_url;
}
  1. access_token 및 사용자 정보 요청
@RequestMapping("/google_redirect")
public String google_redirect(HttpServletRequest request) {
    // 구글에서 전달해준 code, state 값 가져오기
    String code = request.getParameter("code");
    String state = request.getParameter("state");

	// 세션에 저장해둔 state값 가져오기
    String session_state = String.valueOf(request.getSession().getAttribute("state"));
	
	// CSRF 공격 방지를 위해 state 값 비교
    if (!state.equals(session_state)) {
        System.out.println("세션 불일치");
        request.getSession().removeAttribute("state");
        return "/sns/sns_error";
    }

    String tokenURL = "https://oauth2.googleapis.com/token";
    String client_id = [CLIENT ID];
    String client_secret = [CLIENT SECRET];
    String redirect_uri = {contextPath}"/google_redirect";

    // body data 생성
    MultiValueMap<String, String> parameter = new LinkedMultiValueMap<>();
    parameter.add("grant_type", "authorization_code");
    parameter.add("client_id", client_id);
    parameter.add("client_secret", client_secret);
    parameter.add("code", code);
    parameter.add("redirect_uri", redirect_uri);

    // request header 설정
    HttpHeaders headers = new HttpHeaders();
    // Content-type을 application/x-www-form-urlencoded 로 설정
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    // header 와 body로 Request 생성
    HttpEntity<?> entity = new HttpEntity<>(parameter, headers);

    try {
        RestTemplate restTemplate = new RestTemplate();
        // 응답 데이터(json)를 Map 으로 받을 수 있도록 관련 메시지 컨버터 추가
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        // Post 방식으로 Http 요청
        // 응답 데이터 형식은 Hashmap 으로 지정
        ResponseEntity<HashMap> result = restTemplate.postForEntity(tokenURL, entity, HashMap.class);
        Map<String, String> resMap = result.getBody();

        // 리턴받은 access_token 가져오기
        String access_token = resMap.get("access_token");

        String userInfoURL = "https://www.googleapis.com/userinfo/v2/me";
        // Header에 access_token 삽입
        headers.set("Authorization", "Bearer "+access_token);

        // Request entity 생성
        HttpEntity<?> userInfoEntity = new HttpEntity<>(headers);

        // GET 방식으로 Http 요청
        // 응답 데이터 형식은 Hashmap 으로 지정
        ResponseEntity<HashMap> userResult = restTemplate.exchange(userInfoURL, HttpMethod.GET, userInfoEntity, HashMap.class);
        Map<String, String> userResultMap = userResult.getBody();

        //응답 데이터 확인
        System.out.println(userResultMap);

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

    request.getSession().removeAttribute("state");

    return "/sns/sns_result";
}
profile
Java, Spring, SpringMVC, JPA, MyBatis

0개의 댓글

Powered by GraphCDN, the GraphQL CDN