Uwierzytelnianie potwierdza tożsamość użytkownika i jest zwykle nazywane rejestracją lub logowaniem. Autoryzacja to proces przyznawania lub odmawiania dostępu do danych lub zasobów. Na przykład Twoja aplikacja prosi użytkownika o zgodę na dostęp do jego Dysku Google.
Wywołania uwierzytelniania i autoryzacji muszą być 2 oddzielnymi procesami, które są dostosowane do potrzeb aplikacji.
Jeśli Twoja aplikacja ma funkcje, które mogą korzystać z danych Google API, ale nie są wymagane jako podstawowe funkcje aplikacji, zaprojektuj ją tak, aby mogła prawidłowo obsługiwać przypadki, w których dane API są niedostępne. Możesz na przykład ukryć listę ostatnio zapisanych plików, gdy użytkownik nie przyznał dostępu do Dysku.
O dostęp do zakresów, które są potrzebne do korzystania z interfejsów API Google, należy prosić 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, 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 proszeni są 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 interfejsu AuthorizationClient.
Konfigurowanie projektu w konsoli Google Cloud
- Otwórz projekt w Cloud Console, lub utwórz projekt, jeśli go jeszcze nie masz.
- Na stronie Branding,
upewnij się, że wszystkie informacje są kompletne i prawidłowe.
- Sprawdź, czy Twoja aplikacja ma przypisaną prawidłową nazwę, logo i stronę główną. Te wartości będą wyświetlane użytkownikom na ekranie zgody Zaloguj się przez Google podczas rejestracji oraz na ekranie Aplikacje i usługi innych firm.
- Upewnij się, że masz podane adresy URL polityki prywatności i warunków korzystania z usługi.
- Jeśli nie masz jeszcze identyfikatora klienta Androida dla swojej aplikacji, utwórz go na stronie Klienci. Musisz podać nazwę pakietu aplikacji i podpis SHA-1.
- Otwórz stronę Klienci.
- Kliknij Utwórz klienta.
- Wybierz typ aplikacji Android.
- Wpisz nazwę klienta OAuth. Ta nazwa jest wyświetlana na stronie Klienci projektu, aby identyfikować klienta.
- Wpisz nazwę pakietu aplikacji na Androida. Ta wartość jest zdefiniowana w
packageatrybucie<manifest>elementu w plikuAndroidManifest.xml. - Wpisz odcisk cyfrowy certyfikatu podpisywania SHA-1 dystrybucji aplikacji.
- Jeśli Twoja aplikacja korzysta z podpisywania aplikacji przez Google Play, skopiuj odcisk SHA-1 ze strony podpisywania aplikacji w Konsoli Play.
- Jeśli zarządzasz własnym magazynem kluczy i kluczami podpisywania, użyj narzędzia keytool
dołączonego do Javy, aby wydrukować informacje o certyfikacie w formacie
czytelnym dla człowieka. Skopiuj wartość
SHA-1w sekcjiCertificate fingerprintsdanych wyjściowych narzędzia keytool. Więcej informacji znajdziesz w dokumentacji Uwierzytelnianie klienta w interfejsach API Google na Androida. - (Opcjonalnie) Potwierdź własność aplikacji na Androida.
- Jeśli nie masz jeszcze identyfikatora klienta „Aplikacja internetowa”,
utwórz go na stronie Klienci. Na razie możesz zignorować pola „Autoryzowane źródła JavaScript” i „Autoryzowane identyfikatory URI przekierowania”. Ten identyfikator klienta będzie używany do identyfikowania serwera backendu podczas komunikacji z usługami uwierzytelniania Google.
- Otwórz stronę Klienci.
- Kliknij Utwórz klienta.
- Wybierz typ Aplikacja internetowa.
Potwierdzanie prawa własności do aplikacji
Możesz potwierdzić własność aplikacji, aby zmniejszyć ryzyko podszywania się pod nią.
Aby dokończyć proces weryfikacji, możesz użyć swojego konta dewelopera w Google Play, jeśli je masz, a Twoja aplikacja jest zarejestrowana w Konsoli Google Play. Aby weryfikacja się powiodła, musisz spełnić te wymagania:
- Musisz mieć zarejestrowaną aplikację w Konsoli Google Play o tej samej nazwie pakietu i odcisku cyfrowym certyfikatu podpisywania SHA-1 co klient Android OAuth, w przypadku którego przeprowadzasz weryfikację.
- Musisz mieć uprawnienia administratora w odniesieniu do aplikacji w Konsoli Google Play. Dowiedz się więcej o zarządzaniu dostępem w Konsoli Google Play.
Aby dokończyć proces weryfikacji, w sekcji Potwierdź własność aplikacji klienta Androida kliknij przycisk Potwierdź własność.
Jeśli weryfikacja się powiedzie, zobaczysz powiadomienie potwierdzające jej pomyślne zakończenie. W przeciwnym razie pojawi się komunikat o błędzie.
Aby naprawić błąd weryfikacji, wykonaj te czynności:
- Upewnij się, że aplikacja, którą weryfikujesz, jest zarejestrowana w Konsoli Google Play.
- Sprawdź, czy masz uprawnienia administratora w odniesieniu do aplikacji w Konsoli Google Play.
Deklarowanie zależności
W pliku build.gradle modułu zadeklaruj zależności, używając najnowszej wersji biblioteki Google Identity Services.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.5.1"
}
Prośba o uprawnienia wymagane przez działania użytkownika
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 wymagające dostępu do miejsca na dane aplikacji na Dysku, 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.launch(IntentSenderRequest.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ługuj odpowiedź w sposób pokazany w tym fragmencie kodu, w którym 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 (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 {
// 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 wyślesz 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.
Unieważnianie uprawnień do danych użytkownika lub zasobów
Aby unieważnić wcześniej przyznany dostęp, wywołaj funkcję
AuthorizationClient.revokeAccess(). Jeśli na przykład użytkownik usuwa swoje konto z Twojej aplikacji, a Twoja aplikacja miała wcześniej dostęp do DriveScopes.DRIVE_FILE, użyj tego kodu, aby unieważnić 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));
Czyszczenie pamięci podręcznej tokenów
Tokeny dostępu OAuth są zapisywane w pamięci podręcznej lokalnie po otrzymaniu ich 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ę nieprawidłowe z innych powodów.
Jeśli podczas używania tokena otrzymasz IllegalStateException, wyczyść pamięć podręczną lokalną, aby mieć pewność, że następne żądanie autoryzacji tokena dostępu zostanie wysłane do serwera OAuth. Ten fragment kodu usuwa invalidAccessToken z pamięci podręcznej lokalnej:
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));
Uzyskiwanie informacji o użytkowniku podczas autoryzacji
Odpowiedź autoryzacji nie zawiera żadnych informacji o koncie użytkownika, którego użyto. Zawiera tylko token dla żądanych zakresów. Na przykład odpowiedź na żądanie tokena dostępu do Dysku Google użytkownika nie ujawnia tożsamości konta wybranego przez użytkownika, chociaż 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, masz te opcje:
Zaloguj użytkownika na jego konto Google za pomocą interfejsów Credential Manager API, zanim poprosisz o autoryzację. Odpowiedź uwierzytelniania z Credential Manager zawiera informacje o użytkowniku, takie jak adres e-mail, a także ustawia domyślne konto aplikacji na wybrane konto. W razie potrzeby możesz śledzić to konto w swojej aplikacji. Kolejne żądanie autoryzacji używa tego konta jako domyślnego i pomija krok wyboru konta w procesie autoryzacji. Aby użyć innego konta do autoryzacji, zobacz Autoryzacja z konta innego niż domyślne.
W żądaniu autoryzacji oprócz zakresów, które Cię interesują (na przykład,
Drive scope), poproś o zakresyuserinfo,profile, iopenid. Po zwróceniu tokena dostępu pobierz informacje o użytkowniku, wysyłając żądanie HTTPGETdo punktu końcowego OAuth userinfo (https://www.googleapis.com/oauth2/v3/userinfo) za pomocą preferowanej biblioteki HTTP i dołączając token dostępu, który został otrzymany w nagłówku, co odpowiada temu poleceniucurl:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"Odpowiedź to
UserInfoograniczona do żądanych zakresów, sformatowana w formacie JSON.
Autoryzacja z konta innego niż domyślne
Jeśli do uwierzytelniania używasz Credential Manager 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ą używać tego konta domyślnego. Aby wymusić wyświetlenie selektora kont,
wyloguj użytkownika z aplikacji za pomocą interfejsu clearCredentialState() z
Credential Manager.
Utrzymywanie stałego dostępu do danych użytkownika
Jeśli musisz uzyskać dostęp do danych użytkownika z poziomu aplikacji, wywołaj
AuthorizationClient.authorize() tylko raz. W kolejnych sesjach i tak
długo, jak 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 musisz uzyskać dostęp do danych użytkownika w trybie offline z serwera backendu, musisz poprosić o inny typ tokena, czyli „token odświeżania”.
Tokeny dostępu są celowo projektowane tak, aby miały krótki okres ważności, czyli 1 godzinę. Jeśli token dostępu zostanie przechwycony lub naruszony, jego ograniczony okres ważności minimalizuje potencjalne nadużycia. Po wygaśnięciu token staje się nieprawidłowy, a wszelkie próby jego użycia zostaną odrzucone przez serwer zasobów. Ponieważ tokeny dostępu mają krótki okres ważności, serwery używają tokenów odświeżania, aby utrzymać stały dostęp do danych użytkownika. Tokeny odświeżania to tokeny o długim okresie ważności, których klient używa do żądania od serwera autoryzacji tokena dostępu o krótkim okresie ważności, gdy stary token dostępu wygaśnie, bez interakcji z użytkownikiem.
Aby uzyskać token odświeżania, musisz najpierw uzyskać kod autoryzacji podczas procesu autoryzacji w aplikacji, prosząc o „dostęp offline”, a następnie wymienić kod autoryzacji na token odświeżania na serwerze. Konieczne jest bezpieczne przechowywanie na serwerze tokenów odświeżania o długim okresie ważności, 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 należy je przechowywać na serwerach backendu aplikacji, gdzie następuje wymiana na token dostępu.
Gdy kod autoryzacji zostanie wysłany na serwer backendu aplikacji, możesz go wymienić na serwerze na token dostępu o krótkim okresie ważności i token odświeżania o długim okresie ważności, wykonując czynności opisane w przewodniku po autoryzacji konta. Ta wymiana powinna odbywać się tylko w 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.launch(IntentSenderRequest.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"));
Ten fragment kodu zakłada, że autoryzacja jest uruchamiana 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
}
});
}