개요
목적: 이 문서에서는 Google Workspace API가 제공하는 일반적인 OAuth 2.0 기능을 설명합니다. 사용할 수 있습니다 이러한 함수는 인증 및 승인에 사용할 수 있습니다.
GoogleCredential
를 사용하여 OAuth 2.0 승인을 수행하는 방법에 대한 안내
Google 서비스에 대한 자세한 내용은
Java용 Google API 클라이언트 라이브러리에서 OAuth 2.0 사용
요약: OAuth 2.0은 최종 사용자가 안전하게 클라이언트를 승인할 수 있는 표준 사양 안전하게 서버 측 리소스에 액세스할 수 있습니다. 또한 OAuth 2.0 Bearer 토큰 사양은 액세스를 사용하여 보호되는 리소스에 액세스하는 방법을 설명합니다 최종 사용자 인증 프로세스 중에 부여되는 토큰입니다.
자세한 내용은 다음 패키지에 대한 Javadoc 문서를 참고하세요.
- com.google.api.client.auth.oauth2 (google-oauth-client에서)
- com.google.api.client.extensions.servlet.auth.oauth2 (from google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
클라이언트 등록
Java용 Google OAuth 클라이언트 라이브러리를 사용하려면 먼저 인증 서버에 애플리케이션을 등록하여 클라이언트 ID를 받고 클라이언트 보안 비밀번호 이 프로세스에 대한 일반적인 정보는 고객 등록 사양을 참조하세요.
사용자 인증 정보 및 사용자 인증 정보 저장소
사용자 인증 정보
는
액세스할 수 있습니다. 갱신 토큰을 사용하면 Credential
도 액세스 권한을 새로고침합니다.
토큰을 만료해야 합니다. 예를 들어
액세스 토큰이 있는 경우 다음과 같은 방법으로 요청할 수 있습니다.
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
대부분의 애플리케이션은 사용자 인증 정보의 액세스 토큰을 유지하고 향후 승인으로 리디렉션되지 않도록 토큰 새로고침 페이지로 이동합니다. 이 CredentialStore 이 라이브러리의 구현은 지원 중단되었으며 향후 삭제될 예정입니다. 지원합니다 대안은 DataStoreFactory 및 DataStore 인코더-디코더 StoredCredential 이러한 API는 Java용 Google HTTP 클라이언트 라이브러리.
라이브러리에서 제공되는 다음 구현 중 하나를 사용할 수 있습니다.
- JdoDataStoreFactory JDO를 사용하여 사용자 인증 정보를 유지합니다
- AppEngineDataStoreFactory Google App Engine Data Store API를 사용하여 사용자 인증 정보를 유지합니다.
- MemoryDataStoreFactory '유지' 메모리의 크리덴셜 정보를 저장할 수 있으며, 이는 단기적인 수명 주기 동안 사용할 수 있는 스토리지 용량을 제공합니다
- FileDataStoreFactory 사용자 인증 정보를 파일에 유지합니다
Google App Engine 사용자:
AppEngineCredentialStore가 지원 중단되었으며 삭제될 예정입니다.
이때 AppEngineDataStoreFactory StoredCredential을 사용합니다. 사용자 인증 정보를 이전 방식으로 저장한 경우 추가된 도우미 메서드를 사용할 수 있습니다. migrateTo(AppEngineDataStoreFactory) 또는 migrateTo(DataStore) 마이그레이션합니다
DataStoreCredentialRefreshListener 사용 공개 IP 주소를 사용하여 GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
승인 코드 흐름
승인 코드 흐름을 사용하여 최종 사용자가 애플리케이션에 권한을 부여할 수 있도록 허용 액세스할 수 있도록 합니다. 이 흐름의 프로토콜은 승인 코드 부여 사양
이 흐름은 AuthorizationCodeFlow에 응답합니다. 단계는 다음과 같습니다.
- 최종 사용자가 애플리케이션에 로그인합니다. 해당 사용자를 사용자 ID를 생성합니다.
- 전화걸기 AuthorizationCodeFlow.loadCredential(String), 사용자 ID를 기반으로 하여 사용자의 인증 정보를 이미 알고 있는지 확인합니다. 표시된 경우 완료된 것입니다.
- 그렇지 않은 경우 AuthorizationCodeFlow.newAuthorizationUrl()을 호출합니다. 최종 사용자의 브라우저를 인증 페이지로 안내하여 액세스할 수 있습니다.
- 그러면 웹브라우저에서 '코드'가 포함된 리디렉션 URL로 리디렉션합니다. 쿼리 매개변수를 사용하여 AuthorizationCodeFlow.newTokenRequest(String),
- 사용 AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) 사용자 인증 정보를 저장하고 가져올 수 있습니다.
또는 kube-APIserver와 AuthorizationCodeFlow, 하위 수준의 클래스를 사용할 수 있습니다.
- DataStore.get(String) 사용 사용자 ID를 기준으로 스토어에서 사용자 인증 정보를 로드합니다.
- AuthorizationCodeRequestUrl 사용 브라우저를 승인 페이지로 이동시킵니다.
- AuthorizationCodeResponseUrl 사용 승인 응답을 처리하고 승인 코드를 파싱합니다.
- AuthorizationCodeTokenRequest 사용 액세스 토큰과 갱신 토큰을 요청할 수 있습니다.
- 새 사용자 인증 정보를 만들고 DataStore.set(String, V)를 사용하여 저장합니다.
- 사용자 인증 정보를 사용하여 보호된 리소스에 액세스합니다. 만료된 액세스 토큰은 있습니다. 다음을 사용하세요. DataStoreCredentialRefreshListener 공개 IP 주소를 사용하여 Credential.Builder.addRefreshListener(CredentialRefreshListener).
Servlet 인증 코드 흐름
이 라이브러리는 서블릿 도우미 클래스를 제공하여 인증 코드 플로우를 자세히 설명합니다. 구체적인 서브클래스를 제공하기만 하면 / AbstractAuthorizationCodeServlet 및 AbstractAuthorizationCodeCallbackServlet (google-oauth-client-servlet에서 가져옴) web.xml 파일에 추가합니다. 여전히 사용자를 직접 처리해야 합니다. 로그인하고 사용자 ID를 추출합니다.
샘플 코드
public class ServletSample extends AbstractAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Google App Engine 승인 코드 흐름
App Engine의 승인 코드 흐름은 서블릿과 거의 동일합니다. Google App Engine의 Users Java API. Users Java API를 사용하려면 사용자가 로그인해야 합니다. 대상: 사용자를 로그인 페이지로 리디렉션하는 방법에 대한 정보 로그인한 후 다음을 확인하세요. 보안 및 인증 (web.xml에 있음).
서블릿 사례와의 가장 큰 차이점은
서브클래스로
AbstractAppEngineAuthorizationCodeServlet 및 AbstractAppEngineAuthorizationCodeCallbackServlet (google-oauth-client-appengine에서) 추상 서블릿 클래스를 확장하고 Users Java API를 사용하여 getUserId
메서드를 자동으로 구현합니다. Java용 Google HTTP 클라이언트 라이브러리의 AppEngineDataStoreFactory는 Google App Engine Data Store API를 사용하여 사용자 인증 정보를 유지하기에 좋은 옵션입니다.
샘플 코드
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
명령줄 승인 코드 흐름
간단한 예시 코드: dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
브라우저 기반 클라이언트 흐름
다음은 암시적 부여 사양:
- BrowserClientRequestUrl 사용 최종 사용자의 브라우저를 승인 페이지로 리디렉션합니다. 여기서 최종 사용자가 애플리케이션에 보호 대상 데이터에 대한 액세스 권한을 부여할 수 있습니다.
- JavaScript 애플리케이션을 사용하여 URL에 있는 액세스 토큰을 처리합니다. 부분 URL은 인증 서버에 등록된 리디렉션 URI에 있습니다.
웹 애플리케이션의 샘플 사용법:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
만료된 액세스 토큰 감지
OAuth 2.0 Bearer 사양에 따라,
액세스 권한이 만료된 보호된 리소스에 액세스하기 위해 서버가 호출될 때
토큰의 경우 서버는 일반적으로 HTTP 401 Unauthorized
상태 코드로 응답합니다.
다음과 같습니다.
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
그러나 사양에 많은 유연성이 있는 것으로 보입니다. 대상 OAuth 2.0 제공업체의 문서를 확인하세요.
또 다른 방법은 expires_in
매개변수를 확인하는 것입니다.
액세스 토큰 응답
부여된 액세스 토큰의 전체 기간(초)을 지정합니다.
보통 1시간 정도 소요됩니다 하지만 마지막에 액세스 토큰이 실제로 만료되지는 않을 수도 있습니다.
서버에서 계속 액세스를 허용할 수 있습니다. 그래서 저희는
401 Unauthorized
상태 코드를 기다리는 것이 좋습니다.
경과 시간을 기준으로 토큰이 만료되었다고 가정합니다. 또는 다음 작업을 수행할 수 있습니다.
만료 직전에 액세스 토큰을 새로고침해 보세요. 그리고 토큰 서버가
사용할 수 없는 경우 401
를 수신할 때까지 액세스 토큰을 계속 사용하세요. 이
인코더-디코더 아키텍처를
사용자 인증 정보.
또 다른 옵션은 모든 요청 전에 새 액세스 토큰을 가져오는 것이지만 매번 토큰 서버에 추가 HTTP 요청을 해야 하므로 선택의 폭이 넓지 않을 수도 있습니다. 이상적으로는 액세스 토큰을 안전한 영구 스토리지에 보관하여 새로운 액세스에 대한 애플리케이션의 요청을 최소화 토큰입니다. (하지만 설치된 애플리케이션의 경우 보안 저장소가 어려운 문제입니다.)
액세스 토큰은 만료 이외의 이유로 유효하지 않을 수 있습니다. 예를 들어 사용자가 명시적으로 토큰을 취소한 경우 오류 처리 코드가 강력합니다. 토큰이 더 이상 만료되거나 취소된 경우 액세스 권한을 삭제해야 합니다. 삭제할 수 있습니다 예를 들어 Android에서는 AccountManager.invalidateAuthToken을 확인할 수 있습니다.