Kurz: Přihlášení uživatelů a volání Microsoft Graph API z aplikace Android

V tomto kurzu vytvoříte aplikaci Android, která se integruje s Microsoft identity platform pro přihlášení uživatelů a získání přístupového tokenu pro volání Graph API Microsoftu.

Po dokončení tohoto kurzu vaše aplikace přijme přihlášení osobních účtů Microsoft (včetně outlook.com, live.com a dalších) a pracovních nebo školních účtů z libovolné společnosti nebo organizace, která používá Azure Active Directory.

V tomto kurzu:

  • Vytvoření projektu aplikace Android v Android Studio
  • Registrace aplikace v Azure Portal
  • Přidání kódu pro podporu přihlášení a odhlášení uživatele
  • Přidání kódu pro volání Graph API Microsoftu
  • Otestování aplikace

Požadavky

  • Android Studio 3,5+

Jak tento kurz funguje

Shows how the sample app generated by this tutorial works

Aplikace v tomto kurzu se přihlásí uživatele a získá data jejich jménem. Tato data budou přístupná prostřednictvím chráněného rozhraní API (Microsoft Graph API), které vyžadují autorizaci a jsou chráněna Microsoft identity platform.

A konkrétně:

  • Aplikace se přihlásí uživatele prostřednictvím prohlížeče nebo Microsoft Authenticator a Portál společnosti Intune.
  • Koncový uživatel přijme oprávnění, která vaše aplikace požadovala.
  • Vaše aplikace bude vystavena přístupový token pro Microsoft Graph API.
  • Přístupový token bude součástí požadavku HTTP na webové rozhraní API.
  • Zpracujte odpověď Microsoft Graph.

Tato ukázka používá knihovnu Microsoft Authentication Library pro Android (MSAL) k implementaci ověřování: com.microsoft.identity.client.

Služba MSAL automaticky prodlouží tokeny, zajistí jednotné přihlašování (SSO) mezi jinými aplikacemi na zařízení a bude spravovat účty.

Tento kurz ukazuje zjednodušené příklady práce s MSAL pro Android. Pro zjednodušení používá pouze režim jednoho účtu. Pokud chcete prozkoumat složitější scénáře, podívejte se na dokončenou ukázku funkčního kódu na GitHub.

Vytvoření projektu

Pokud ještě nemáte Android aplikaci, nastavte nový projekt podle těchto kroků.

  1. Otevřete Android Studio a vyberte Spustit nový projekt Android Studio.
  2. Vyberte Základní aktivitu a vyberte Další.
  3. Pojmenujte svoji aplikaci.
  4. Uložte název balíčku. Později ho zadáte do Azure Portal.
  5. Změňte jazyk z Kotlin na Javu.
  6. Nastavte minimální úroveň rozhraní API na rozhraní API 19 nebo vyšší a klikněte na Dokončit.
  7. V zobrazení projektu zvolte v rozevíracím seznamu Project pro zobrazení zdrojových a nesourcových souborů projektu, otevřete aplikaci/build.gradle a nastavte targetSdkVersion na 28.

Integrace s knihovnou Microsoft Authentication Library

Registrace aplikace

  1. Přihlaste se k webu Azure Portal.

  2. Pokud máte přístup k více tenantům, pomocí filtru Adresáře a předplatná v horní nabídce přepněte na tenanta, ve kterém chcete aplikaci zaregistrovat.

  3. Vyhledejte a vyberte Azure Active Directory.

  4. V části Spravovat vyberte Registrace aplikací>Znovit registraci.

  5. Zadejte název aplikace. Uživatelé vaší aplikace můžou vidět tento název a později ho můžete změnit.

  6. Vyberte Zaregistrovat.

  7. V části Spravovat vyberte Možnost Přidat>platformu>Android.

  8. Zadejte název balíčku projektu. Pokud jste si stáhli kód, je tato hodnota com.azuresamples.msalandroidapp.

  9. V části Hash podpisu na stránce Konfigurovat aplikaci Android vyberte Generovat hodnotu hash vývojového podpisu. A zkopírujte příkaz KeyTool, který chcete použít pro vaši platformu.

    KeyTool.exe se instaluje jako součást sady Java Development Kit (JDK). Abyste mohli spustit příkaz KeyTool, musíte také nainstalovat nástroj OpenSSL. Další informace najdete v dokumentaci k Android generování klíče.

  10. Zadejte hodnotu hash podpisu vygenerovanou nástrojem KeyTool.

  11. Vyberte Konfigurovat a uložte konfiguraci MSAL, která se zobrazí na stránce konfigurace Android, abyste ji mohli zadat při pozdější konfiguraci aplikace.

  12. Vyberte Hotovo.

Konfigurace aplikace

  1. V podokně projektu Android Studio přejděte do aplikace\src\main\res.

  2. Klikněte pravým tlačítkem na res a zvolte Nový>adresář. Zadejte raw název nového adresáře a klikněte na tlačítko OK.

  3. V aplikaci>src>main>res>raw vytvořte nový soubor JSON s názvem auth_config_single_account.json a vložte konfiguraci MSAL, kterou jste si uložili dříve.

    Pod identifikátor URI přesměrování vložte:

      "account_mode" : "SINGLE",
    

    Konfigurační soubor by měl vypadat podobně jako v tomto příkladu:

    {
      "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"
          }
        }
      ]
    }
    

    Tento kurz ukazuje, jak nakonfigurovat aplikaci v režimu jednoho účtu. Další informace o režimu jednoho nebo více účtů a konfiguraci aplikace najdete v dokumentaci.

  4. Vhlavním>AndroidManifest.xmlaplikace>> přidejte BrowserTabActivity do textu aplikace následující aktivitu. Tato položka umožňuje Microsoftu volat zpět do vaší aplikace po dokončení ověřování:

    <!--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>
    

    Hodnotu nahraďte názvem balíčku, který jste zaregistrovali v Azure Portalandroid:host=. Nahraďte hodnotu hodnotou hash klíče, kterou jste zaregistrovali v Azure Portalandroid:path=. Hodnota hash podpisu by neměla být zakódovaná adresou URL. Ujistěte se, že je na začátku hodnoty hash podpisu úvodní / hodnota.

    Název balíčku, který nahradíte android:host hodnotou, by měla vypadat nějak takto: com.azuresamples.msalandroidapp. Hodnota Hash podpisu, kterou nahradíte android:path , by měla vypadat nějak takto: /1wIqXSqBj7w+h11ZifsnqwgyKrY=.

    Tyto hodnoty najdete také v okně Ověřování registrace vaší aplikace. Všimněte si, že identifikátor URI přesměrování bude vypadat nějak takto: msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D. I když je hodnota Hash podpisu zakódovaná na konci této hodnoty, hodnota hash podpisu by neměla být zakódovaná do vaší android:path hodnoty.

Použití knihovny MSAL

Přidání knihovny MSAL do projektu

  1. V okně Android Studio projektu přejděte do souboru app>build.gradle a přidejte následující:

    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")
    }
    

    Další informace o sadě Microsoft Graph SDK

Požadované importy

Na začátek hlavního>>>souboru java>com.example(yourapp)>MainActivity.java přidejte následující:

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.*;

Vytvoření instance aplikace PublicClientApplication

Inicializace proměnných

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 Uvnitř třídy, odkaz na následující onCreate() metoda vytvořit instanci MSAL pomocí 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

Naslouchejte tlačítkům a metodám volání nebo chybám protokolu odpovídajícím způsobem.

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());
            }
        });
    }

Důležité

Odhlášení pomocí nástroje MSAL odebere všechny známé informace o uživateli z aplikace, ale uživatel bude mít na svém zařízení aktivní relaci. Pokud se uživatel pokusí znovu přihlásit, může se zobrazit přihlašovací uživatelské rozhraní, ale nemusí se muset znovu připojit ke svým přihlašovacím údajům, protože relace zařízení je stále aktivní.

getAuthInteractiveCallback

Zpětné volání používané pro interaktivní žádosti.

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

Zpětné volání používané pro bezobslužné žádosti

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);
        }
    };
}

Volání rozhraní API Microsoft Graphu

Následující kód ukazuje, jak volat GraphAPI pomocí sady 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);
                }
            });
}

Přidání uživatelského rozhraní

Aktivita

Pokud chcete modelovat uživatelské rozhraní mimo tento kurz, následující metody poskytují průvodce aktualizací textu a naslouchání tlačítkům.

updateUI

Povolte nebo zakažte tlačítka na základě stavu přihlášení a nastavte text.

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 aktualizace textu v uživatelském rozhraní tak, aby odrážela odhlášení.

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

Layout

Ukázkový activity_main.xml soubor pro zobrazení tlačítek a textových polí

<?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>

Testování aplikace

Spuštění v místním prostředí

Sestavte a nasaďte aplikaci do testovacího zařízení nebo emulátoru. Měli byste být schopni se přihlásit a získat tokeny pro Azure AD nebo osobní účty Microsoft.

Po přihlášení aplikace zobrazí data vrácená z koncového bodu Microsoft Graph/me. PR 4

Při prvním přihlášení uživatele k vaší aplikaci se zobrazí výzva identitě Microsoftu, aby souhlasila s požadovanými oprávněními. Někteří Azure AD tenanti zakázali souhlas uživatele, který vyžaduje, aby správci souhlasili jménem všech uživatelů. Pokud chcete tento scénář podporovat, budete muset buď vytvořit vlastního tenanta, nebo získat souhlas správce.

Vyčištění prostředků

Pokud už ho nepotřebujete, odstraňte objekt aplikace, který jste vytvořili v kroku Registrace aplikace .

Nápověda a podpora

Pokud potřebujete pomoc, chcete nahlásit problém nebo se chcete dozvědět o možnostech podpory, přečtěte si nápovědu a podporu pro vývojáře.

Další kroky

Přečtěte si další informace o vytváření mobilních aplikací, které volají chráněná webová rozhraní API v naší sérii scénářů s více částmi.