0. 구글 개발자 설정
[SpringSecurity] 구글 로그인 연동하기 1 - 구글 서비스 등록
1. GoogleOAuthRequest
import lombok.Builder; import lombok.Data; @Data @Builder public class GoogleOAuthRequest { private String redirectUri; private String clientId; private String clientSecret; private String code; private String responseType; private String scope; private String accessType; private String grantType; private String state; private String includeGrantedScopes; private String loginHint; private String prompt; }
2. GoogleOAuthResponse
import lombok.Data; @Data public class GoogleOAuthResponse { private String accessToken; private String expiresIn; private String refreshToken; private String scope; private String tokenType; private String idToken; }
3. GoolgeVO
import lombok.Data; import lombok.ToString; /** * iss=https://accounts.google.com, azp=65465-scmetkg9rf7g0anodi1g4rfp434f81c7rfu.apps.googleusercontent.com, aud=2383873471124-scmetkg9rf7g0ano5di431g24rf32pf81c7rfu.apps.googleusercontent.com, sub=1012804324239237390221439, email=honggidong@gmail.com, email_verified=true, at_hash=Dv79dcE0q6Ydsadn2uxR5FJpHw, name=홍길동, picture=https://lh3.googleusercontent.com/a/AGNmyxZ-H_5vaMlSUoYZpdXFd134nFl63lJ-gCsJ7icmj3sA=s96-c, given_name=길동, family_name=홍, locale=ko, iat=16818033226, exp=16813806826, alg=RS4256, kid=9697180428796829a92372e7949d1a9fff14231cd61b1e3, typ=JWT * */ @Data @ToString public class GoolgeVO { private String id; private String email; private String name; private String picture ; private String given_name; private String family_name; private String oauth; }
4.GoogleOAuthURL
public class GoogleOAuthURL { 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"; ///login/oauth2/code/google final static String REDIRECT_URI = "http://localhost:8080/login/google/auth"; final static String CLIENT_ID = "클라이언트 아이디"; final static String CLIENT_SECRET = "클라이언트 시크릿"; public static String url(){ StringBuffer sb=new StringBuffer(); sb.append(GOOGLE_AUTH_BASE_URL); sb.append("?client_id="); sb.append(CLIENT_ID); sb.append("&redirect_uri="); sb.append(REDIRECT_URI); sb.append("&response_type=code"); sb.append("&scope=email profile openid https://www.googleapis.com/auth/drive.file"); sb.append("&access_type=offline"); return sb.toString(); } }
5.GoogleOAuthController
import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.korea.webtoon.dao.MemberDAO; @Controller @RequestMapping("/login/google/") public class GoogleOAuthController { @Autowired private MemberDAO memberDao; /** 로그인페이지 로그인 첫 화면 요청 메소드 */ @RequestMapping(value = "login.do", method = { RequestMethod.GET, RequestMethod.POST }) public String join( Model model) { model.addAttribute("urlGoogle",GoogleOAuthURL.url()); return "oauth/google/login"; } /** * Authentication Code를 전달 받는 엔드포인트 **/ @GetMapping("auth") public String googleAuth(Model model, @RequestParam(value = "code") String authCode) throws JsonProcessingException { // HTTP Request를 위한 RestTemplate RestTemplate restTemplate = new RestTemplate(); // Google OAuth Access Token 요청을 위한 파라미터 세팅 GoogleOAuthRequest googleOAuthRequestParam = GoogleOAuthRequest.builder().clientId(GoogleOAuthURL.CLIENT_ID) .clientSecret(GoogleOAuthURL.CLIENT_SECRET).code(authCode).redirectUri("http://localhost:8080/login/google/auth") .grantType("authorization_code").build(); // JSON 파싱을 위한 기본값 세팅 // 요청시 파라미터는 스네이크 케이스로 세팅되므로 Object mapper에 미리 설정해준다. ObjectMapper mapper = new ObjectMapper(); mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); mapper.setSerializationInclusion(Include.NON_NULL); // AccessToken 발급 요청 ResponseEntity<String> resultEntity = restTemplate.postForEntity(GoogleOAuthURL.GOOGLE_TOKEN_BASE_URL, googleOAuthRequestParam, String.class); // Token Request GoogleOAuthResponse result = mapper.readValue(resultEntity.getBody(), new TypeReference<GoogleOAuthResponse>() {}); // ID Token만 추출 (사용자의 정보는 jwt로 인코딩 되어있다) String jwtToken = result.getIdToken(); String requestUrl = UriComponentsBuilder.fromHttpUrl("https://oauth2.googleapis.com/tokeninfo") .queryParam("id_token", jwtToken).encode().toUriString(); String resultJson = restTemplate.getForObject(requestUrl, String.class); Map<String, String> userInfo = mapper.readValue(resultJson, new TypeReference<Map<String, String>>() {}); System.out.println("userInfo 정보 " +userInfo.toString()); GoolgeVO googleVO=new GoolgeVO(); googleVO.setId("google_"+userInfo.get("kid")); googleVO.setEmail(userInfo.get("email")); googleVO.setName(userInfo.get("name")); googleVO.setPicture(userInfo.get("picture")); googleVO.setOauth("google"); //DB 에 저장 및 업데이트 처리 memberDao.saveOauthGoole(googleVO); model.addAllAttributes(userInfo); model.addAttribute("token", result.getAccessToken()); System.out.println(userInfo); return "oauth/google/loginSuccess"; } /** * 토큰 무효화 **/ @GetMapping("revoke/token") @ResponseBody public Map<String, String> revokeToken(@RequestParam(value = "token") String token) throws JsonProcessingException { Map<String, String> result = new HashMap<>(); RestTemplate restTemplate = new RestTemplate(); final String requestUrl = UriComponentsBuilder.fromHttpUrl(GoogleOAuthURL.GOOGLE_REVOKE_TOKEN_BASE_URL) .queryParam("token", token).encode().toUriString(); System.out.println("TOKEN ? " + token); String resultJson = restTemplate.postForObject(requestUrl, null, String.class); result.put("result", "success"); result.put("resultJson", resultJson); return result; } }
6.login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>google OAuth</title> <style type="text/css"> #container{width:500px} .sns_join a.google{border-color:#eb6155} .sns_join a{display:block;margin-top:10px;width:100%;height:50px;border:1px solid #000;font-size:15px;line-height:50px;text-align:center;background:#fff} .sns_join a.google>.icon{background:url(https://cdn.jsdelivr.net/gh/braverokmc79/ouath2-img@v1.0.0/images/icon_google.png) no-repeat 0 0;background-size:18px auto} .sns_join a>.icon{display:inline-block;margin:0 auto;padding-left:29px;width:188px;color:#666;font-size:15px;letter-spacing:-1px;line-height:20px;text-align:left} </style> </head> <body> <div id="container"> <div class="sns_join"> <a class="google" href="${urlGoogle}" id="googleLoginBtn"> <span class="icon">구글 로그인</span> </a> </div> </div> </body> <script> const onClickGoogleLogin = (e) => { window.location.replace('${src}') } const googleLoginBtn = document.getElementById("googleLoginBtn"); googleLoginBtn.addEventListener("click", onClickGoogleLogin) </script> </html>
7.loginSuccess.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>Google Login 완료</h1> <div>${token}</div> <div>${email}</div> <div> <img src="${picture}"></img> </div> <div> <a href="/">홈</a> </div> </body> </html>
db 저장 참조
<insert id="saveOauthGoole"> MERGE INTO WEBTOON_USER USING DUAL ON (EMAIL = #{email} ) WHEN MATCHED THEN UPDATE SET OAUTH = #{oauth} WHEN NOT MATCHED THEN INSERT (USER_IDX, NAME, ID, EMAIL, OAUTH) VALUES( seq_user_idx.nextVal, #{name}, #{id}, #{email}, #{oauth} ) </insert>
댓글 ( 4)
댓글 남기기