[ KOSTA 교육 47일차 ] Google OAuth Flow ( Code -> Token -> UserInfo ) | 세션, 쿠키 | WAS의 Filter, Spring Interceptor

junjun·2024년 7월 5일
0

KOSTA

목록 보기
45/48

[Google Client API]

구글 API에서 제공하는 라이브러리를 사용하여 Google의 REST 서비스를 사용하는 코드입니다.

GoogleCredential credential = new GoogleCredential()
					.setAccessToken(accessToken);
Plus plus = Plus.builder(new NetHttpTransport(),
				GsonFactory.getDefaultInstance(),
                credential)
                .setApplicationName("Google-PlusAmple/1.0")
                .build();
  • 구글이 OAuth를 위해 제공하는 라이브러리 (jar)
    • 상업용으로 한다면 구글이 제공하는게 안전합니다.
    • Google API Docs를 보며 진행해야합니다.
    • API Spec이 바뀌면 갈아 엎어야할 수도 있습니다.

이런 jar파일을 쓰면
스프링 프레임워크를 사용하지 않아도
OAuth 2.0 앤드포인트를 호출할 수 있습니다.

1) Java 기본 코드로 호출
2) Google이 제공하는 라이브러리 (jar) 호출
3) Spring Framework의 RestTemplate을 통해 호출 ( OpenFeign 도 고려 가능 )

Google OAuth Flow

  • Google Rest Service를 사용하기 위한 토큰을 얻을 수 있는 플로우입니다.

  • 크게
    코드 발급 / 토큰 발급 / 토큰을 이용한 유저의 정보 가져오기
    단계로 나눌 수 있습니다.

1단계. 사용자가 우리 서버의 로그인 폼으로 요청

  • 승인에 필요한 매개변수를 담은 VO/DTO를 생성한다.
    • callback으로 code를 발급받습니다.
@PropertySource("classpath:lec14-oauth.properties")
@Controller
public class GooglCallTestCtl {
		
     @Value("${google.loginform.url}")
     private String LOGIN_FORM_URL;
     
     @Value("${google.client.id}")
     private String CLIENT_ID;
     
     @Value("${google.client.pw}")
     private String CLIENT_PW;
     
     //@Value("${google.redirect.uri}")
     private String CALLBACK_URL = "http://localhost:8089/test_google_oauth2callback";
     
    @Value("${google.endpoint.token}")
    private String ENDPOINT_URL_TOKEN;
    
    @Value("${google.endpoint.userinfo}")
    private String ENDPOINT_URL_USERINFO;


	@RequestMapping(value="/test_google_loginForm")
    public String loginForm(Model model){
    	String loginFormUrl = LOGIN_FORM_URL
        					+ "?client_id=" + CLIENT_ID
                            + "&redirect_uri=" + CALLBACK_URL
                            + "&response_type=code"
                            + "&scope=email%20profile%20openid"
                            + "&access_type=offline";
        return "redirect:" + loginFormUrl;
    }

}
  • GET https://accounts.google.com/o/oauth2/v2/auth
  • 이 엔드포인트에 대해 추가적으로 보내줘야하는 매개변수(Request Param/Data)
    • client_id
      - 필수 항목으로, Google OAuth Service에서 우리 어플리케이션의 ID를 등록해야합니다.
    • redirect_uri
      • 필수 항목으로, 사용자의 승인 흐름을 완료한 후 우리 어플리케이션 서버가 사용자를 리다이렉션하는 위치를 결정합니다.
      • client_id의 승인된 리다이렉션 URI와 일치하지 않으면, redirect_uri_mismatch 오류가 발생합니다.
    • response_type
      • Google Oauth 2.0 엔드포인트에서 승인 코드를 반환하는지 여부를 결정합니다.
      • 웹 어플리케이션의 경우 매개변수 값을 code로 결정합니다.
    • scope
      • 어플리케이션에서 Access할 수 있는 리소스를 식별하는, 공백으로 구분된 범위 목록
      • 이러한 목록을 기반으로 구글에서 사용자에게 표시하는 동의 화면을 나타냄.
      • 필요한 리소스에 대한 엑세스만 요청 가능, 사용자는 애플리케이션에 부여하는 액세스 권한의 양을 제어할 수 있음.
    • access_type

2단계. Google의 OAuth 2.0 서버의 로그인 폼으로 리다이렉트 (code 발급)

@RequestMapping(value="/test_google_oauth2callback", method=RequestMethod.GET)
public void loginGoogle(@RequestParam("code") String code){
	System.out.println("authCode = " + code);
}
  • Google이 저희 서버 ( 구글 입장에서 CLIENT )가 미리 등록해둔 redirect_uri 로 데이터를 전달해줍니다.

3단계. code를 통해 accessToken / refreshToken 획득

@RequestMapping(value="/test_google_oauth2callback", method=RequestMethod.GET)
public void loginGoogle(@RequestParam("code") String code){
	System.out.println("authCode = " + code);
    
    GoogleRequestVO googleRequestVO = GoogleRequestVO.builder()
    										.code(authCode)
                                            .clientId(CLIENT_ID)
                                            .clientSecrety(CLIENT_PW)
                                            .grantType("authorization_code")
                                            .redirectUri(CALLBACK_URL).build();
    
    RestTemplate restTemplate = new RestTemplate(); // @Autowired로 받아와도 됨
    ResponseEntity<GoogleResponseVO> tokenResponse = 
    restTemplate.postForEntity(ENDPOINT_URL_TOKEN, googleRequestVO,GoogleResponseVO.class);
}

```java
// 코드를 활용해 token을 받아오는데 사용하는 GoogleRequestVO.class
@Data
@Builder
public class GoogleRequest {
	private String clientId;
    private String redirectUri;
    private String clientSecret;
    private String responseType;
    private String scope;
    private String code;
    private String accessType;
    private String grantType;
    private String state;
    private String includeGrantedScopes;
    private String loginHint;
    private String prompt;
}
// GoogleResponse.class => Code를 통해 Token을 받아오는데 사용하는 VO
@Data
@NoArgsConstructor
public class GoogleResponse {
	private String accessToken;	// 애플리케이션이 Google API 요청을 승인하기 위해 보내는 토큰
    private String expiresIn;	// Access Token의 남은 수명
    private String refreshToken; // 새 액세스 토큰을 얻는 데 사용할 수 있는 토큰
    private String scope;
    private String tokenType;	// 반환된 토큰 유형 (Bearer)
    private String idToken;
}

4단계. 발급받은 토큰의 유효성 검사 & 유저의 데이터 가져오기

@Data
@NoArgsConstructor
public class GoogleUserInfoResponse {
	private String iss;
    private String azp;
    private String aud;
    private String sub;
    private String email;
    private String email_verified;
    private String at_hash;
    private String name;
    private String picture;
    private String given_name;
    private String family_name;
    private String locale;
    private String iat;
    private String exp;
    private String alg;
    private String kid;
    private String typ;
}


// CallTest.java
ResponseEntity<GoogleUserInfoResponse> googleUserInfoResponse
		= restTemplate.postForEntity("https://oauth2.googleapis.com/tokeninfo", map, GoogleUserInfoResponse.class);
        
String email = googleUserInfoResponse.getBody().getEmail();
return email;

Filter & Interceptor

  • 인증, 인가
    • 인증 : 회원인가 아닌가 검증
    • 인가 : 인증은 되었는데, 이 회원이 어디까지 사용할 수 있는지
      - 해당 회원이 쓸 수 있는 권한을 어느범위까지 해줄 수 있는지?

0개의 댓글