Samouczek: logowanie użytkowników i wywoływanie Microsoft interfejs Graph API z aplikacji systemu Android

W tym samouczku utworzysz aplikację dla systemu Android zintegrowaną z Platforma tożsamości Microsoft, aby zalogować użytkowników i uzyskać token dostępu w celu wywołania Microsoft interfejs Graph API.

Po ukończeniu tego samouczka aplikacja zaakceptuje logowania osobistych kont Microsoft (w tym outlook.com, live.com i innych), a także konta służbowe z dowolnej firmy lub organizacji korzystającej z usługi Azure Active Directory.

W tym samouczku:

  • Tworzenie projektu aplikacji systemu Android w programie Android Studio
  • Rejestrowanie aplikacji w Azure Portal
  • Dodawanie kodu do obsługi logowania użytkownika i wylogowywanie
  • Dodawanie kodu w celu wywołania Microsoft interfejs Graph API
  • Testowanie aplikacji

Wymagania wstępne

  • Android Studio 3.5+

Jak działa ten samouczek

Pokazuje, jak działa przykładowa aplikacja wygenerowana przez ten samouczek

Aplikacja w tym samouczku zaloguje użytkowników i pobierze dane w ich imieniu. Te dane będą dostępne za pośrednictwem chronionego interfejsu API (Microsoft interfejs Graph API), który wymaga autoryzacji i jest chroniony przez Platforma tożsamości Microsoft.

Więcej szczegółów:

  • Aplikacja zaloguje się za pośrednictwem przeglądarki lub Microsoft Authenticator i Intune — Portal firmy.
  • Użytkownik końcowy zaakceptuje żądane uprawnienia aplikacji.
  • Aplikacja zostanie wystawiona jako token dostępu dla Microsoft interfejs Graph API.
  • Token dostępu zostanie uwzględniony w żądaniu HTTP do internetowego interfejsu API.
  • Przetwórz odpowiedź Microsoft Graph.

W tym przykładzie użyto biblioteki uwierzytelniania Microsoft dla systemu Android (MSAL) do zaimplementowania uwierzytelniania: com.microsoft.identity.client.

Biblioteka MSAL będzie automatycznie odnawiać tokeny, dostarczać logowanie jednokrotne (SSO) między innymi aplikacjami na urządzeniu i zarządzać kontami.

W tym samouczku przedstawiono uproszczone przykłady pracy z biblioteką MSAL dla systemu Android. Dla uproszczenia używa tylko trybu pojedynczego konta. Aby zapoznać się z bardziej złożonymi scenariuszami, zobacz ukończony przykładowy kod roboczy w usłudze GitHub.

Tworzenie projektu

Jeśli nie masz jeszcze aplikacji dla systemu Android, wykonaj następujące kroki, aby skonfigurować nowy projekt.

  1. Otwórz program Android Studio i wybierz pozycję Uruchom nowy projekt programu Android Studio.
  2. Wybierz pozycję Podstawowe działanie i wybierz przycisk Dalej.
  3. Nadaj nazwę aplikacji.
  4. Zapisz nazwę pakietu. Później przejdziesz do Azure Portal.
  5. Zmień język z Kotlin na Java.
  6. Ustaw wartość Minimalny poziom interfejsu API na interfejs API 19 lub nowszy, a następnie kliknij przycisk Zakończ.
  7. W widoku projektu wybierz pozycję Projekt na liście rozwijanej, aby wyświetlić pliki projektów źródłowych i innych niż źródłowe, otwórz plik app/build.gradle i ustaw wartość targetSdkVersion28.

Integracja z biblioteką uwierzytelniania Microsoft

Rejestrowanie aplikacji

  1. Zaloguj się w witrynie Azure Portal.

  2. Jeśli masz dostęp do wielu dzierżaw, użyj filtru Katalogi i subskrypcje w górnym menu, aby przełączyć się do dzierżawy, w której chcesz zarejestrować aplikację.

  3. Wyszukaj i wybierz pozycję Azure Active Directory.

  4. W obszarze Zarządzanie wybierz pozycję Rejestracje aplikacji>Nowa rejestracja.

  5. Wprowadź nazwę aplikacji. Użytkownicy aplikacji mogą zobaczyć tę nazwę i później ją zmienić.

  6. Wybierz pozycję Zarejestruj.

  7. W obszarze Zarządzanie wybierz pozycję Uwierzytelnianie>Dodaj platformę>Android.

  8. Wprowadź nazwę pakietu projektu. Jeśli pobrano kod, ta wartość to com.azuresamples.msalandroidapp.

  9. W sekcji Skrót podpisu na stronie Konfigurowanie aplikacji systemu Android wybierz pozycję Generowanie skrótu sygnatury programistycznej i skopiuj polecenie KeyTool do użycia dla platformy.

    KeyTool.exe jest instalowany w ramach zestawu Java Development Kit (JDK). Aby wykonać polecenie KeyTool, należy również zainstalować narzędzie OpenSSL. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją systemu Android dotyczącą generowania klucza .

  10. Wprowadź skrót Signature wygenerowany przez usługę KeyTool.

  11. Wybierz pozycję Konfiguruj i zapisz konfigurację biblioteki MSAL wyświetlaną na stronie konfiguracji systemu Android , aby można było ją wprowadzić podczas konfigurowania aplikacji później.

  12. Kliknij Gotowe.

Konfigurowanie aplikacji

  1. W okienku projektu programu Android Studio przejdź do pozycji app\src\main\res.

  2. Kliknij prawym przyciskiem myszy pozycję res i wybierz pozycję Nowy>katalog. Wprowadź raw jako nazwę nowego katalogu, a następnie kliknij przycisk OK.

  3. W pliku>src>main>res>raw utwórz nowy plik JSON o nazwie auth_config_single_account.json i wklej zapisaną wcześniej konfigurację biblioteki MSAL.

    Poniżej identyfikatora URI przekierowania wklej:

      "account_mode" : "SINGLE",
    

    Plik konfiguracji powinien przypominać następujący przykład:

    {
      "client_id" : "0984a7b6-bc13-4141-8b0d-8f767e136bb7",
      "authorization_user_agent" : "DEFAULT",
      "redirect_uri" : "msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D",
      "broker_redirect_uri_registered" : true,
      "account_mode" : "SINGLE",
      "authorities" : [
        {
          "type": "AAD",
          "audience": {
            "type": "AzureADandPersonalMicrosoftAccount",
            "tenant_id": "common"
          }
        }
      ]
    }
    

    W tym samouczku pokazano tylko, jak skonfigurować aplikację w trybie pojedynczego konta. Zapoznaj się z dokumentacją, aby uzyskać więcej informacji na temat trybu pojedynczego lub wielu kont i konfigurowania aplikacji

  4. W pliku src >main>AndroidManifest.xmlaplikacji> dodaj BrowserTabActivity działanie poniżej do treści aplikacji. Ten wpis umożliwia Microsoft wywołanie z powrotem do aplikacji po zakończeniu uwierzytelniania:

    <!--Intent filter to capture System Browser or Authenticator calling back to our app after sign-in-->
    <activity
        android:name="com.microsoft.identity.client.BrowserTabActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="msauth"
                android:host="Enter_the_Package_Name"
                android:path="/Enter_the_Signature_Hash" />
        </intent-filter>
    </activity>
    

    Zastąp nazwę pakietu zarejestrowaną w Azure Portal wartościandroid:host=. Zastąp skrót klucza zarejestrowany w Azure Portal wartościandroid:path=. Skrót podpisu nie powinien być zakodowany pod adresem URL. Upewnij się, że na początku skrótu podpisu znajduje się wiodący./

    Wartość "Nazwa pakietu" zastąpisz android:host wartością podobną do: com.azuresamples.msalandroidapp. Wartość "Skrót podpisu" zostanie zamienina android:path na podobną do: /1wIqXSqBj7w+h11ZifsnqwgyKrY=.

    Te wartości można również znaleźć w bloku Uwierzytelnianie rejestracji aplikacji. Pamiętaj, że identyfikator URI przekierowania będzie wyglądać podobnie do: msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D. Skrót podpisu jest zakodowany pod adresem URL na końcu tej wartości, ale skrót podpisu nie powinien być zakodowany pod adresem URL w wartości android:path .

Korzystanie z biblioteki MSAL

Dodawanie biblioteki MSAL do projektu

  1. W oknie projektu programu Android Studio przejdź do pozycji app>build.gradle i dodaj następujące elementy:

    apply plugin: 'com.android.application'
    
    allprojects {
     repositories {
        mavenCentral()
        google()
        mavenLocal()
        maven {
            url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1'
        }
        maven {
            name "vsts-maven-adal-android"
            url "https://identitydivision.pkgs.visualstudio.com/_packaging/AndroidADAL/maven/v1"
            credentials {
                username System.getenv("ENV_VSTS_MVN_ANDROIDADAL_USERNAME") != null ? System.getenv("ENV_VSTS_MVN_ANDROIDADAL_USERNAME") : project.findProperty("vstsUsername")
                password System.getenv("ENV_VSTS_MVN_ANDROIDADAL_ACCESSTOKEN") != null ? System.getenv("ENV_VSTS_MVN_ANDROIDADAL_ACCESSTOKEN") : project.findProperty("vstsMavenAccessToken")
            }
        }
        jcenter()
     }
    }
    dependencies{
     implementation 'com.microsoft.identity.client:msal:2.+'
     implementation 'com.microsoft.graph:microsoft-graph:1.5.+'
     }
    packagingOptions{
     exclude("META-INF/jersey-module-version")
    }
    

    Więcej informacji na temat zestawu SDK programu Graph Microsoft

Wymagane importy

Dodaj następujące elementy do góry głównej aplikacji>java>>>java com.example(yourapp)>MainActivity.java

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.JsonObject;
import com.microsoft.graph.authentication.IAuthenticationProvider; //Imports the Graph sdk Auth interface
import com.microsoft.graph.concurrency.ICallback;
import com.microsoft.graph.core.ClientException;
import com.microsoft.graph.http.IHttpRequest;
import com.microsoft.graph.models.extensions.*;
import com.microsoft.graph.requests.extensions.GraphServiceClient;
import com.microsoft.identity.client.AuthenticationCallback; // Imports MSAL auth methods
import com.microsoft.identity.client.*;
import com.microsoft.identity.client.exception.*;

Tworzenie wystąpienia elementu PublicClientApplication

Inicjowanie zmiennych

private final static String[] SCOPES = {"Files.Read"};
/* Azure AD v2 Configs */
final static String AUTHORITY = "https://login.microsoftonline.com/common";
private ISingleAccountPublicClientApplication mSingleAccountApp;

private static final String TAG = MainActivity.class.getSimpleName();

/* UI & Debugging Variables */
Button signInButton;
Button signOutButton;
Button callGraphApiInteractiveButton;
Button callGraphApiSilentButton;
TextView logTextView;
TextView currentUserTextView;

Oncreate

MainActivity Wewnątrz klasy zapoznaj się z następującą metodą onCreate(), aby utworzyć wystąpienie biblioteki MSAL przy użyciu klasy SingleAccountPublicClientApplication.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initializeUI();

    PublicClientApplication.createSingleAccountPublicClientApplication(getApplicationContext(),
            R.raw.auth_config_single_account, new IPublicClientApplication.ISingleAccountApplicationCreatedListener() {
                @Override
                public void onCreated(ISingleAccountPublicClientApplication application) {
                    mSingleAccountApp = application;
                    loadAccount();
                }
                @Override
                public void onError(MsalException exception) {
                    displayError(exception);
                }
            });
}

loadAccount

//When app comes to the foreground, load existing account to determine if user is signed in
private void loadAccount() {
    if (mSingleAccountApp == null) {
        return;
    }

    mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() {
        @Override
        public void onAccountLoaded(@Nullable IAccount activeAccount) {
            // You can use the account data to update your UI or your app database.
            updateUI(activeAccount);
        }

        @Override
        public void onAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount) {
            if (currentAccount == null) {
                // Perform a cleanup task as the signed-in account changed.
                performOperationOnSignOut();
            }
        }

        @Override
        public void onError(@NonNull MsalException exception) {
            displayError(exception);
        }
    });
}

initializeUI

Nasłuchuj przycisków i odpowiednio wywołaj metody lub błędy dziennika.

private void initializeUI(){
        signInButton = findViewById(R.id.signIn);
        callGraphApiSilentButton = findViewById(R.id.callGraphSilent);
        callGraphApiInteractiveButton = findViewById(R.id.callGraphInteractive);
        signOutButton = findViewById(R.id.clearCache);
        logTextView = findViewById(R.id.txt_log);
        currentUserTextView = findViewById(R.id.current_user);

        //Sign in user
        signInButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                if (mSingleAccountApp == null) {
                    return;
                }
                mSingleAccountApp.signIn(MainActivity.this, null, SCOPES, getAuthInteractiveCallback());
            }
        });

        //Sign out user
        signOutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mSingleAccountApp == null){
                    return;
                }
                mSingleAccountApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() {
                    @Override
                    public void onSignOut() {
                        updateUI(null);
                        performOperationOnSignOut();
                    }
                    @Override
                    public void onError(@NonNull MsalException exception){
                        displayError(exception);
                    }
                });
            }
        });

        //Interactive
        callGraphApiInteractiveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mSingleAccountApp == null) {
                    return;
                }
                mSingleAccountApp.acquireToken(MainActivity.this, SCOPES, getAuthInteractiveCallback());
            }
        });

        //Silent
        callGraphApiSilentButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mSingleAccountApp == null){
                    return;
                }
                mSingleAccountApp.acquireTokenSilentAsync(SCOPES, AUTHORITY, getAuthSilentCallback());
            }
        });
    }

Ważne

Wylogowywanie przy użyciu biblioteki MSAL usuwa wszystkie znane informacje o użytkowniku z aplikacji, ale użytkownik nadal będzie miał aktywną sesję na swoim urządzeniu. Jeśli użytkownik spróbuje zalogować się ponownie, może zobaczyć interfejs użytkownika logowania, ale może nie być konieczne ponowne wprowadzenie poświadczeń, ponieważ sesja urządzenia jest nadal aktywna.

getAuthInteractiveCallback

Wywołanie zwrotne używane do żądań interakcyjnych.

private AuthenticationCallback getAuthInteractiveCallback() {
    return new AuthenticationCallback() {
        @Override
        public void onSuccess(IAuthenticationResult authenticationResult) {
            /* Successfully got a token, use it to call a protected resource - MSGraph */
            Log.d(TAG, "Successfully authenticated");
            /* Update UI */
            updateUI(authenticationResult.getAccount());
            /* call graph */
            callGraphAPI(authenticationResult);
        }

        @Override
        public void onError(MsalException exception) {
            /* Failed to acquireToken */
            Log.d(TAG, "Authentication failed: " + exception.toString());
            displayError(exception);
        }
        @Override
        public void onCancel() {
            /* User canceled the authentication */
            Log.d(TAG, "User cancelled login.");
        }
    };
}

getAuthSilentCallback

Wywołanie zwrotne używane do żądań dyskretnych

private SilentAuthenticationCallback getAuthSilentCallback() {
    return new SilentAuthenticationCallback() {
        @Override
        public void onSuccess(IAuthenticationResult authenticationResult) {
            Log.d(TAG, "Successfully authenticated");
            callGraphAPI(authenticationResult);
        }
        @Override
        public void onError(MsalException exception) {
            Log.d(TAG, "Authentication failed: " + exception.toString());
            displayError(exception);
        }
    };
}

Wywoływanie interfejsu API programu Microsoft Graph

Poniższy kod przedstawia sposób wywoływania interfejsu GraphAPI przy użyciu zestawu Graph SDK.

callGraphAPI

private void callGraphAPI(IAuthenticationResult authenticationResult) {

    final String accessToken = authenticationResult.getAccessToken();

    IGraphServiceClient graphClient =
            GraphServiceClient
                    .builder()
                    .authenticationProvider(new IAuthenticationProvider() {
                        @Override
                        public void authenticateRequest(IHttpRequest request) {
                            Log.d(TAG, "Authenticating request," + request.getRequestUrl());
                            request.addHeader("Authorization", "Bearer " + accessToken);
                        }
                    })
                    .buildClient();
    graphClient
            .me()
            .drive()
            .buildRequest()
            .get(new ICallback<Drive>() {
                @Override
                public void success(final Drive drive) {
                    Log.d(TAG, "Found Drive " + drive.id);
                    displayGraphResult(drive.getRawObject());
                }

                @Override
                public void failure(ClientException ex) {
                    displayError(ex);
                }
            });
}

Dodawanie interfejsu użytkownika

Działanie

Jeśli chcesz modelować interfejs użytkownika poza tym samouczkiem, poniższe metody zawierają przewodnik dotyczący aktualizowania tekstu i nasłuchiwania przycisków.

updateUI

Włącz/wyłącz przyciski na podstawie stanu logowania i ustaw tekst.

private void updateUI(@Nullable final IAccount account) {
    if (account != null) {
        signInButton.setEnabled(false);
        signOutButton.setEnabled(true);
        callGraphApiInteractiveButton.setEnabled(true);
        callGraphApiSilentButton.setEnabled(true);
        currentUserTextView.setText(account.getUsername());
    } else {
        signInButton.setEnabled(true);
        signOutButton.setEnabled(false);
        callGraphApiInteractiveButton.setEnabled(false);
        callGraphApiSilentButton.setEnabled(false);
        currentUserTextView.setText("");
        logTextView.setText("");
    }
}

displayError

private void displayError(@NonNull final Exception exception) {
       logTextView.setText(exception.toString());
   }

displayGraphResult

private void displayGraphResult(@NonNull final JsonObject graphResponse) {
      logTextView.setText(graphResponse.toString());
  }

performOperationOnSignOut

Metoda aktualizowania tekstu w interfejsie użytkownika w celu odzwierciedlenia wylogowania.

private void performOperationOnSignOut() {
    final String signOutText = "Signed Out.";
    currentUserTextView.setText("");
    Toast.makeText(getApplicationContext(), signOutText, Toast.LENGTH_SHORT)
            .show();
}

Layout

Przykładowy activity_main.xml plik do wyświetlania przycisków i pól tekstowych.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:weightSum="10">

        <Button
            android:id="@+id/signIn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:gravity="center"
            android:text="Sign In"/>

        <Button
            android:id="@+id/clearCache"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:gravity="center"
            android:text="Sign Out"
            android:enabled="false"/>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/callGraphInteractive"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:text="Get Graph Data Interactively"
            android:enabled="false"/>

        <Button
            android:id="@+id/callGraphSilent"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            android:text="Get Graph Data Silently"
            android:enabled="false"/>
    </LinearLayout>

    <TextView
        android:text="Getting Graph Data..."
        android:textColor="#3f3f3f"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:id="@+id/graphData"
        android:visibility="invisible"/>

    <TextView
        android:id="@+id/current_user"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="20dp"
        android:layout_weight="0.8"
        android:text="Account info goes here..." />

    <TextView
        android:id="@+id/txt_log"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="20dp"
        android:layout_weight="0.8"
        android:text="Output goes here..." />
</LinearLayout>

Testowanie aplikacji

Uruchamianie lokalnie

Skompiluj i wdróż aplikację na urządzeniu testowym lub emulatorze. Powinno być możliwe zalogowanie się i uzyskanie tokenów dla kont Azure AD lub osobistych Microsoft.

Po zalogowaniu aplikacja wyświetli dane zwrócone z punktu końcowego Microsoft Graph/me. Żądanie ściągnięcia 4

Przy pierwszym zalogowaniu się użytkownika do aplikacji zostanie wyświetlony monit Microsoft tożsamości, aby wyrazić zgodę na żądane uprawnienia. Niektóre dzierżawy Azure AD wyłączyły zgodę użytkownika, co wymaga zgody administratorów w imieniu wszystkich użytkowników. Aby obsługiwać ten scenariusz, musisz utworzyć własną dzierżawę lub otrzymać zgodę administratora.

Czyszczenie zasobów

Gdy obiekt aplikacji utworzony w kroku Zarejestruj aplikację nie będzie już potrzebny, usuń obiekt aplikacji.

Pomoc i obsługa techniczna

Jeśli potrzebujesz pomocy, chcesz zgłosić problem lub poznać opcje pomocy technicznej, zobacz Pomoc i obsługa techniczna dla deweloperów.

Następne kroki

Dowiedz się więcej o tworzeniu aplikacji mobilnych wywołujących chronione internetowe interfejsy API w naszej wieloczęściowej serii scenariuszy.