Uwierzytelnianie potwierdza tożsamość użytkownika i jest często określane jako rejestracja lub logowanie. Autoryzacja to proces przyznawania lub odrzucania dostępu do danych lub zasobów. Na przykład aplikacja prosi użytkownika o zgodę na dostęp do jego Dysku Google.
Wywołania uwierzytelniania i autoryzacji powinny być 2 osobnymi i odrębnymi procesami, które zależą od potrzeb aplikacji.
Jeśli Twoja aplikacja ma funkcje, które mogą korzystać z danych interfejsu Google API, ale nie są wymagane jako podstawowe funkcje aplikacji, zaprojektuj ją tak, aby mogła sobie poradzić w sytuacjach, gdy dane interfejsu API są niedostępne. Możesz na przykład ukryć listę ostatnio zapisanych plików, jeśli użytkownik nie przyznał dostępu do Dysku.
Prośby o dostęp do zakresów, które są potrzebne do uzyskania dostępu do interfejsów API Google, należy wysyłać tylko wtedy, gdy użytkownik wykona działanie wymagające dostępu do konkretnego interfejsu API. Na przykład należy poprosić o uprawnienia dostępu do Dysku użytkownika za każdym razem, gdy kliknie on przycisk „Zapisz na Dysku”.
Oddzielenie autoryzacji od uwierzytelniania pozwala uniknąć przytłoczenia nowych użytkowników lub wprowadzenia ich w błąd co do tego, dlaczego są proszeni o określone uprawnienia.
Do uwierzytelniania zalecamy używanie interfejsu Credential Manager API. Do autoryzowania działań, które wymagają dostępu do danych użytkownika przechowywanych przez Google, zalecamy używanie AuthorizationClient.
Konfigurowanie projektu
- Otwórz projekt w lub utwórz projekt, jeśli jeszcze go nie masz.
- Na stronie upewnij się, że wszystkie informacje są kompletne i prawidłowe.
- Upewnij się, że aplikacja ma przypisaną prawidłową nazwę, logo i stronę główną. Te wartości będą wyświetlane użytkownikom na ekranie zgody funkcji Zaloguj się przez Google podczas rejestracji oraz na ekranie aplikacji i usług innych firm.
- Upewnij się, że masz podane adresy URL polityki prywatności i warunków korzystania z usługi w swojej aplikacji.
- W utwórz identyfikator klienta Androida dla swojej aplikacji, jeśli jeszcze go nie masz. Musisz podać nazwę pakietu aplikacji i podpis SHA-1.
- W sekcji utwórz nowy identyfikator klienta „Aplikacja internetowa”, jeśli jeszcze go nie masz. Pola „Autoryzowane źródła JavaScript” i „Autoryzowane identyfikatory URI przekierowania” możesz na razie zignorować. Ten identyfikator klienta będzie używany do identyfikowania serwera backendu podczas komunikacji z usługami uwierzytelniania Google.
Deklarowanie zależności
W pliku build.gradle modułu zadeklaruj zależności, używając najnowszej wersji biblioteki usług tożsamości Google.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.4.0"
}
Prośby o uprawnienia wymagane przez działania użytkownika
Za każdym razem, gdy użytkownik wykona działanie wymagające dodatkowego zakresu, wywołaj funkcję
AuthorizationClient.authorize()
. Jeśli na przykład użytkownik wykona działanie, które wymaga dostępu do pamięci aplikacji Dysk, wykonaj te czynności:
Kotlin
val requestedScopes: List<Scope> = listOf(DriveScopes.DRIVE_FILE)
val authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.build()
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequestBuilder.build())
.addOnSuccessListener { authorizationResult ->
if (authorizationResult.hasResolution()) {
val pendingIntent = authorizationResult.pendingIntent
// Access needs to be granted by the user
startAuthorizationIntent.launchIntentSenderRequest.Builder(pendingIntent!!.intentSender).build()
} else {
// Access was previously granted, continue with user action
saveToDriveAppFolder(authorizationResult);
}
}
.addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.build();
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequest)
.addOnSuccessListener(authorizationResult -> {
if (authorizationResult.hasResolution()) {
// Access needs to be granted by the user
startAuthorizationIntent.launch(
new IntentSenderRequest.Builder(
authorizationResult.getPendingIntent().getIntentSender()
).build()
);
} else {
// Access was previously granted, continue with user action
saveToDriveAppFolder(authorizationResult);
}
})
.addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));
Podczas definiowania ActivityResultLauncher
obsłuż odpowiedź w sposób pokazany w tym fragmencie kodu. Zakładamy, że jest to robione we fragmencie. Kod sprawdza, czy wymagane uprawnienia zostały przyznane, a następnie wykonuje działanie użytkownika.
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// ...
startAuthorizationIntent =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
try {
// extract the result
val authorizationResult = Identity.getAuthorizationClient(requireContext())
.getAuthorizationResultFromIntent(activityResult.data)
// continue with user action
saveToDriveAppFolder(authorizationResult);
} catch (ApiException e) {
// log exception
}
}
}
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
activityResult -> {
try {
// extract the result
AuthorizationResult authorizationResult =
Identity.getAuthorizationClient(requireActivity())
.getAuthorizationResultFromIntent(activityResult.getData());
// continue with user action
saveToDriveAppFolder(authorizationResult);
} catch (ApiException e) {
// log exception
}
});
}
Jeśli uzyskujesz dostęp do interfejsów API Google po stronie serwera, wywołaj metodę getServerAuthCode()
z AuthorizationResult
, aby uzyskać kod autoryzacji, który wysyłasz do backendu w celu wymiany na token dostępu i token odświeżania. Więcej informacji znajdziesz w artykule Utrzymywanie stałego dostępu do danych użytkownika.
Odebranie uprawnień do danych lub zasobów użytkownika
Aby cofnąć wcześniej przyznany dostęp, zadzwoń pod numer AuthorizationClient.revokeAccess()
. Jeśli na przykład użytkownik usuwa swoje konto z Twojej aplikacji, a aplikacja miała wcześniej przyznany dostęp do DriveScopes.DRIVE_FILE
, użyj tego kodu, aby cofnąć dostęp:
Kotlin
val requestedScopes: MutableList<Scope> = mutableListOf(DriveScopes.DRIVE_FILE)
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
.setAccount(account)
.setScopes(requestedScopes)
.build()
Identity.getAuthorizationClient(activity)
.revokeAccess(revokeAccessRequest)
.addOnSuccessListener { Log.i(TAG, "Successfully revoked access") }
.addOnFailureListener { e -> Log.e(TAG, "Failed to revoke access", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
.setAccount(account)
.setScopes(requestedScopes)
.build();
Identity.getAuthorizationClient(activity)
.revokeAccess(revokeAccessRequest)
.addOnSuccessListener(unused -> Log.i(TAG, "Successfully revoked access"))
.addOnFailureListener(e -> Log.e(TAG, "Failed to revoke access", e));
Wyczyść pamięć podręczną tokenów
Tokeny dostępu OAuth są buforowane lokalnie po otrzymaniu z serwera, co przyspiesza dostęp i zmniejsza liczbę wywołań sieciowych. Te tokeny są automatycznie usuwane z pamięci podręcznej po wygaśnięciu, ale mogą też stać się nieważne z innych powodów.
Jeśli podczas używania tokena otrzymasz IllegalStateException
, wyczyść lokalną pamięć podręczną, aby mieć pewność, że następne żądanie autoryzacji tokena dostępu zostanie wysłane na serwer OAuth. Ten fragment kodu usuwa invalidAccessToken
z pamięci podręcznej:
Kotlin
Identity.getAuthorizationClient(activity)
.clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
.addOnSuccessListener { Log.i(TAG, "Successfully removed the token from the cache") }
.addOnFailureListener{ e -> Log.e(TAG, "Failed to clear token", e) }
Java
Identity.getAuthorizationClient(activity)
.clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
.addOnSuccessListener(unused -> Log.i(TAG, "Successfully removed the token from the cache"))
.addOnFailureListener(e -> Log.e(TAG, "Failed to clear the token cache", e));
Pobieranie informacji o użytkowniku podczas autoryzacji
Odpowiedź autoryzacyjna nie zawiera żadnych informacji o koncie użytkownika, które zostało użyte. Zawiera tylko token dla żądanych zakresów. Na przykład odpowiedź na żądanie uzyskania tokena dostępu do Dysku Google użytkownika nie ujawnia tożsamości konta wybranego przez użytkownika, mimo że można jej użyć do uzyskania dostępu do plików na Dysku użytkownika. Aby uzyskać informacje takie jak imię i nazwisko lub adres e-mail użytkownika, możesz skorzystać z tych opcji:
Zaloguj użytkownika za pomocą jego konta Google, korzystając z interfejsów Credential Manager API, zanim poprosisz go o autoryzację. Odpowiedź uwierzytelniająca z Menedżera danych logowania zawiera informacje o użytkowniku, takie jak adres e-mail, a także ustawia wybrane konto jako domyślne konto aplikacji. W razie potrzeby możesz śledzić to konto w aplikacji. Kolejna prośba o autoryzację używa konta jako domyślnego i pomija krok wyboru konta w procesie autoryzacji. Aby użyć innego konta do autoryzacji, zapoznaj się z sekcją Autoryzacja z użyciem konta innego niż domyślne.
W żądaniu autoryzacji oprócz zakresów, które chcesz uzyskać (np.
Drive scope
), poproś o zakresyuserinfo
,profile
iopenid
. Po otrzymaniu tokena dostępu pobierz informacje o użytkowniku, wysyłającGET
żądanie HTTP do punktu końcowego OAuth userinfocurl
(https://www.googleapis.com/oauth2/v3/userinfo) za pomocą wybranej biblioteki HTTP i dołączając otrzymany token dostępu w nagłówku, co jest równoważne z tym poleceniemcurl
:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"
Odpowiedź to
UserInfo
ograniczony do zakresów, o które prosisz, w formacie JSON.
Autoryzacja z konta, które nie jest domyślne
Jeśli do uwierzytelniania używasz Menedżera danych logowania i uruchomisz AuthorizationClient.authorize()
, domyślne konto aplikacji zostanie ustawione na konto wybrane przez użytkownika. Oznacza to, że wszystkie kolejne wywołania autoryzacji będą korzystać z tego konta domyślnego. Aby wymusić wyświetlenie selektora kont, wyloguj użytkownika z aplikacji za pomocą interfejsu clearCredentialState()
API z Credential Manager.
zachować stały dostęp do danych użytkownika;
Jeśli chcesz uzyskać dostęp do danych użytkownika z poziomu aplikacji, wywołaj funkcję AuthorizationClient.authorize()
tylko raz. W kolejnych sesjach i dopóki użytkownik nie usunie przyznanych uprawnień, wywołuj tę samą metodę, aby uzyskać token dostępu i osiągnąć swoje cele bez interakcji z użytkownikiem. Jeśli natomiast chcesz uzyskać dostęp do danych użytkownika w trybie offline z serwera backendu, musisz poprosić o inny typ tokena, zwany „tokenem odświeżania”.
Tokeny dostępu są celowo projektowane tak, aby miały krótki okres ważności, który wynosi 1 godzinę. Jeśli token dostępu zostanie przechwycony lub przejęty, jego ograniczony okres ważności zminimalizuje potencjalne nadużycia. Po wygaśnięciu token staje się nieważny, a wszelkie próby jego użycia są odrzucane przez serwer zasobów. Tokeny dostępu mają krótki okres ważności, dlatego serwery używają tokenów odświeżania, aby utrzymać ciągły dostęp do danych użytkownika. Tokeny odświeżania to tokeny o długim okresie ważności, które są używane przez klienta do żądania od serwera autoryzacji krótkotrwałego tokena dostępu po wygaśnięciu starego tokena dostępu, bez interakcji z użytkownikiem.
Aby uzyskać token odświeżania, musisz najpierw uzyskać kod autoryzacji podczas autoryzacji w aplikacji, prosząc o „dostęp offline”, a następnie wymienić go na token odświeżania na serwerze. Długoterminowe tokeny odświeżania muszą być bezpiecznie przechowywane na serwerze, ponieważ można ich wielokrotnie używać do uzyskiwania nowych tokenów dostępu. Z tego powodu zdecydowanie odradzamy przechowywanie tokenów odświeżania na urządzeniu ze względu na kwestie bezpieczeństwa. Zamiast tego powinny być przechowywane na serwerach backendu aplikacji, gdzie następuje wymiana na token dostępu.
Po wysłaniu kodu autoryzacji na serwer backendu aplikacji możesz wymienić go na serwerze na krótkoterminowy token dostępu i długoterminowy token odświeżania, wykonując czynności opisane w przewodniku po autoryzacji konta. Wymiana powinna odbywać się tylko na backendzie aplikacji.
Kotlin
// Ask for offline access during the first authorization request
val authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.requestOfflineAccess(serverClientId)
.build()
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequest)
.addOnSuccessListener { authorizationResult ->
startAuthorizationIntent.launchIntentSenderRequest.Builder(
pendingIntent!!.intentSender
).build()
}
.addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
// Ask for offline access during the first authorization request
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.requestOfflineAccess(serverClientId)
.build();
Identity.getAuthorizationClient(getContext())
.authorize(authorizationRequest)
.addOnSuccessListener(authorizationResult -> {
startAuthorizationIntent.launch(
new IntentSenderRequest.Builder(
authorizationResult.getPendingIntent().getIntentSender()
).build()
);
})
.addOnFailureListener(e -> Log.e(TAG, "Failed to authorize"));
Poniższy fragment zakłada, że autoryzacja jest rozpoczynana z fragmentu.
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// ...
startAuthorizationIntent =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
try {
val authorizationResult = Identity.getAuthorizationClient(requireContext())
.getAuthorizationResultFromIntent(activityResult.data)
// short-lived access token
accessToken = authorizationResult.accessToken
// store the authorization code used for getting a refresh token safely to your app's backend server
val authCode: String = authorizationResult.serverAuthCode
storeAuthCodeSafely(authCode)
} catch (e: ApiException) {
// log exception
}
}
}
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
activityResult -> {
try {
AuthorizationResult authorizationResult =
Identity.getAuthorizationClient(requireActivity())
.getAuthorizationResultFromIntent(activityResult.getData());
// short-lived access token
accessToken = authorizationResult.getAccessToken();
// store the authorization code used for getting a refresh token safely to your app's backend server
String authCode = authorizationResult.getServerAuthCode()
storeAuthCodeSafely(authCode);
} catch (ApiException e) {
// log exception
}
});
}