Tutorial: Anmelden von Benutzern und Aufrufen der Microsoft Graph-API aus einer Android-Anwendung
Artikel
In diesem Tutorial erstellen Sie eine Android-App, die in Microsoft Entra ID eingebunden wird, um Benutzer anzumelden und ein Zugriffstoken abzurufen, mit dem die Microsoft Graph-API aufgerufen werden kann.
Am Ende dieses Tutorials wird Ihre Anwendung Anmeldungen von persönlichen Microsoft-Konten (einschließlich outlook.com, live.com und anderen) sowie von Geschäfts-, Schul- oder Unikonten von allen Unternehmen oder Organisationen akzeptieren, die Azure AD nutzen.
Dieses Tutorial umfasst folgende Punkte:
Erstellen eines Android-App-Projekts in Android Studio
Registrieren der App im Microsoft Entra Admin Center
Hinzufügen von Code zur Unterstützung der Benutzeranmeldung und -abmeldung
Hinzufügen von Code zum Aufrufen der Microsoft Graph-API
Mit der App in diesem Tutorial werden Benutzer angemeldet und Daten in ihrem Namen abgerufen. Auf diese Daten wird über eine geschützte API (Microsoft Graph-API) zugegriffen, für die eine Autorisierung erforderlich ist und die über Microsoft Identity Platform geschützt ist.
Dieses Beispiel verwendet die Microsoft-Authentifizierungsbibliothek (Microsoft Authentication Library, MSAL) für Android zum Implementieren der Authentifizierung: com.microsoft.identity.client.
Erstellen eines Projekts
Führen Sie diese Schritte aus, um ein neues Projekt zu erstellen, wenn Sie noch keine Android-Anwendung haben.
Öffnen Sie Android Studio, und wählen Sie Start a new Android Studio project (Neues Android Studio-Projekt starten) aus.
Wählen Sie Basic Activity (Standardaktivität) aus, und klicken Sie dann auf Next (Weiter).
Geben Sie einen Namen für die Anwendung ein, z. B. MSALAndroidapp.
Notieren Sie sich den Paketnamen, der in späteren Schritten verwendet werden soll.
Ändern Sie die Sprache von Kotlin in Java.
Legen Sie Mindestebene der SDK-API auf API 16 oder höher fest, und wählen Sie dann Fertig stellen aus.
Registrieren Ihrer Anwendung mit Microsoft Entra ID
Wenn Sie Zugriff auf mehrere Mandanten haben, verwenden Sie das Symbol für Einstellungen im oberen Menü, um zum Mandanten zu wechseln, in dem Sie die Anwendung über das Menü Verzeichnisse + Abonnements registrieren möchten.
Browsen Sie zu Identität>Anwendungen>App-Registrierungen.
Wählen Sie Neue Registrierung aus.
Geben Sie einen Namen für Ihre Anwendung ein. Benutzern Ihrer App wird wahrscheinlich dieser Namen angezeigt. Sie können ihn später ändern.
Wählen Sie bei Unterstützte Kontotypen die Option Konten in allen Organisationsverzeichnissen (beliebiges Microsoft Entra-Verzeichnis: mehrere Mandanten) und persönliche Microsoft-Konten (z. B. Skype, Xbox) aus. Wenn Sie Informationen zu den verschiedenen Kontotypen benötigen, wählen Sie Entscheidungshilfe aus.
Wählen Sie Registrieren.
Wählen Sie unter Verwalten die Optionen Authentifizierung>Plattform hinzufügen>Android aus.
Geben Sie den Paketnamen Ihres Projekts ein. Wenn Sie den Beispielcode heruntergeladen haben, lautet dieser Wert com.azuresamples.msalandroidapp.
Klicken Sie auf der Seite Android-App konfigurieren im Abschnitt Signatur-Hash auf Generieren eines Signatur-Hash für die Entwicklung, und kopieren Sie den KeyTool-Befehl in Ihre Befehlszeile.
„KeyTool. exe“ wird als Teil des Java Development Kit (JDK) installiert. Sie müssen auch das OpenSSL-Tool installieren, um den KeyTool-Befehl auszuführen. Weitere Informationen finden Sie in der Android-Dokumentation zum Generieren eines Schlüssels.
Geben Sie den von KeyTool generierten Signaturhash ein.
Wählen Sie Konfigurieren aus, und speichern Sie die MSAL-Konfiguration, die im Bereich Android-Konfiguration angezeigt wird, damit Sie diese später beim Konfigurieren Ihrer App eingeben können.
Wählen Sie Fertigaus.
Konfigurieren der Anwendung
Navigieren Sie im Projektbereich von Android Studio zu app\src\main\res.
Klicken Sie mit der rechten Maustaste auf res, und wählen Sie New>Directory („Neu“ > „Verzeichnis“) aus. Geben Sie raw als neuen Verzeichnisnamen ein, und wählen Sie OK.
Erstellen Sie unter app>src>main>res>raw eine neue JSON-Datei namens auth_config_single_account.json, und fügen Sie die MSAL-Konfiguration ein, die Sie zuvor gespeichert haben.
Fügen Sie unterhalb des Umleitungs-URIs Folgendes ein:
JSON
"account_mode" : "SINGLE",
Ihre Konfigurationsdatei sollte in etwa wie folgt aussehen:
Es wird die Verwendung von „WEBVIEW“ empfohlen. Wenn Sie „authorization_user_agent“ in Ihrer App als „BROWSER“ konfigurieren möchten, müssen Sie die folgenden Änderungen vornehmen.
a) Aktualisieren Sie „auth_config_single_account.json“ mit: "authorization_user_agent": "Browser".
b) Aktualisieren Sie „AndroidManifest.xml“. Fügen Sie in der App unter app>src>main>AndroidManifest.xml die Aktivität BrowserTabActivity als untergeordnetes Element des <application>-Elements hinzu. Dieser Eintrag ermöglicht Microsoft Entra ID den Rückruf an Ihre Anwendung nach Abschluss der Authentifizierung:
XML
<!--Intent filter to capture System Browser or Authenticator calling back to our app after sign-in--><activityandroid:name="com.microsoft.identity.client.BrowserTabActivity"android:exported="true"><intent-filter><actionandroid:name="android.intent.action.VIEW" /><categoryandroid:name="android.intent.category.DEFAULT" /><categoryandroid:name="android.intent.category.BROWSABLE" /><dataandroid:scheme="msauth"android:host="Enter_the_Package_Name"android:path="/Enter_the_Signature_Hash" /></intent-filter></activity>
Verwenden Sie den Paketnamen, um den Wert android:host=. zu ersetzen. Er sollte wie folgt aussehen: com.azuresamples.msalandroidapp.
Verwenden Sie den Signaturhash, um den Wert android:path= zu ersetzen. Stellen Sie sicher, dass Ihr Signatur-Hash mit einem führenden / beginnt. Er sollte wie folgt aussehen: /aB1cD2eF3gH4+iJ5kL6-mN7oP8q=.
Diese Werte finden Sie ebenfalls auf dem Blatt „Authentifizierung“ Ihrer App-Registrierung.
Hinzufügen von MSAL und relevanten Bibliotheken zu Ihrem Projekt
Navigieren Sie im Android Studio-Projektfenster zu app>build.gradle, und fügen Sie im Abschnitt Abhängigkeiten die folgenden Bibliotheken hinzu:
Öffnen Sie im Android Studio-Projektfenster settings.gradle, und deklarieren Sie das folgende Maven-Repository im Abschnitt dependencyResolutionManagement>repositories:
Wählen Sie in der Benachrichtigungsleiste Jetzt synchronisieren aus.
Erstellen und Aktualisieren des erforderlichen Fragments
In app>src>main>java>com.example(Name Ihrer App). Erstellen Sie die folgenden Android-Fragmente:
MSGraphRequestWrapper
OnFragmentInteractionListener
SingleAccountModeFragment
Öffnen Sie MSGraphRequestWrapper.java, und ersetzen Sie den Code durch den folgenden Codeschnipsel, um die Microsoft Graph-API mithilfe des von MSAL bereitgestellten Tokens aufzurufen:
Java
package com.azuresamples.msalandroidapp;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
publicclassMSGraphRequestWrapper{
privatestaticfinal String TAG = MSGraphRequestWrapper.class.getSimpleName();
// See: https://docs.microsoft.com/en-us/graph/deployments#microsoft-graph-and-graph-explorer-service-root-endpointspublicstaticfinal String MS_GRAPH_ROOT_ENDPOINT = "https://graph.microsoft.com/";
/**
* Use Volley to make an HTTP request with
* 1) a given MSGraph resource URL
* 2) an access token
* to obtain MSGraph data.
**/publicstaticvoidcallGraphAPIUsingVolley(@NonNull final Context context,
@NonNull final String graphResourceUrl,
@NonNull final String accessToken,
@NonNull final Response.Listener<JSONObject> responseListener,
@NonNull final Response.ErrorListener errorListener){
Log.d(TAG, "Starting volley request to graph");
/* Make sure we have a token to send to graph */if (accessToken == null || accessToken.length() == 0) {
return;
}
RequestQueue queue = Volley.newRequestQueue(context);
JSONObject parameters = new JSONObject();
try {
parameters.put("key", "value");
} catch (Exception e) {
Log.d(TAG, "Failed to put parameters: " + e.toString());
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, graphResourceUrl,
parameters, responseListener, errorListener) {
@Overridepublic Map<String, String> getHeaders(){
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + accessToken);
return headers;
}
};
Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());
request.setRetryPolicy(new DefaultRetryPolicy(
3000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
}
}
Öffnen Sie OnFragmentInteractionListener.java, und ersetzen Sie den Code durch den folgenden Codeschnipsel, um die Kommunikation zwischen verschiedenen Fragmenten zu ermöglichen:
Java
package com.azuresamples.msalandroidapp;
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/publicinterfaceOnFragmentInteractionListener{
}
Öffnen Sie SingleAccountModeFragment.java, und ersetzen Sie den Code durch den folgenden Codeschnipsel, um eine Einzelkontoanwendung zu initialisieren, ein Benutzerkonto zu laden und ein Token abzurufen, um die Microsoft Graph-API aufzurufen:
Java
package com.azuresamples.msalandroidapp;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.microsoft.identity.client.AuthenticationCallback;
import com.microsoft.identity.client.IAccount;
import com.microsoft.identity.client.IAuthenticationResult;
import com.microsoft.identity.client.IPublicClientApplication;
import com.microsoft.identity.client.ISingleAccountPublicClientApplication;
import com.microsoft.identity.client.PublicClientApplication;
import com.microsoft.identity.client.SilentAuthenticationCallback;
import com.microsoft.identity.client.exception.MsalClientException;
import com.microsoft.identity.client.exception.MsalException;
import com.microsoft.identity.client.exception.MsalServiceException;
import com.microsoft.identity.client.exception.MsalUiRequiredException;
import org.json.JSONObject;
/**
* Implementation sample for 'Single account' mode.
* <p>
* If your app only supports one account being signed-in at a time, this is for you.
* This requires "account_mode" to be set as "SINGLE" in the configuration file.
* (Please see res/raw/auth_config_single_account.json for more info).
* <p>
* Please note that switching mode (between 'single' and 'multiple' might cause a loss of data.
*/publicclassSingleAccountModeFragmentextendsFragment{
privatestaticfinal String TAG = SingleAccountModeFragment.class.getSimpleName();
/* UI & Debugging Variables */
Button signInButton;
Button signOutButton;
Button callGraphApiInteractiveButton;
Button callGraphApiSilentButton;
TextView scopeTextView;
TextView graphResourceTextView;
TextView logTextView;
TextView currentUserTextView;
TextView deviceModeTextView;
/* Azure AD Variables */private ISingleAccountPublicClientApplication mSingleAccountApp;
private IAccount mAccount;
@Overridepublic View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState){
// Inflate the layout for this fragmentfinal View view = inflater.inflate(R.layout.fragment_single_account_mode, container, false);
initializeUI(view);
// Creates a PublicClientApplication object with res/raw/auth_config_single_account.json
PublicClientApplication.createSingleAccountPublicClientApplication(getContext(),
R.raw.auth_config_single_account,
new IPublicClientApplication.ISingleAccountApplicationCreatedListener() {
@OverridepublicvoidonCreated(ISingleAccountPublicClientApplication application){
/**
* This test app assumes that the app is only going to support one account.
* This requires "account_mode" : "SINGLE" in the config json file.
**/
mSingleAccountApp = application;
loadAccount();
}
@OverridepublicvoidonError(MsalException exception){
displayError(exception);
}
});
return view;
}
/**
* Initializes UI variables and callbacks.
*/privatevoidinitializeUI(@NonNull final View view){
signInButton = view.findViewById(R.id.btn_signIn);
signOutButton = view.findViewById(R.id.btn_removeAccount);
callGraphApiInteractiveButton = view.findViewById(R.id.btn_callGraphInteractively);
callGraphApiSilentButton = view.findViewById(R.id.btn_callGraphSilently);
scopeTextView = view.findViewById(R.id.scope);
graphResourceTextView = view.findViewById(R.id.msgraph_url);
logTextView = view.findViewById(R.id.txt_log);
currentUserTextView = view.findViewById(R.id.current_user);
deviceModeTextView = view.findViewById(R.id.device_mode);
final String defaultGraphResourceUrl = MSGraphRequestWrapper.MS_GRAPH_ROOT_ENDPOINT + "v1.0/me";
graphResourceTextView.setText(defaultGraphResourceUrl);
signInButton.setOnClickListener(new View.OnClickListener() {
publicvoidonClick(View v){
if (mSingleAccountApp == null) {
return;
}
mSingleAccountApp.signIn(getActivity(), null, getScopes(), getAuthInteractiveCallback());
}
});
signOutButton.setOnClickListener(new View.OnClickListener() {
publicvoidonClick(View v){
if (mSingleAccountApp == null) {
return;
}
/**
* Removes the signed-in account and cached tokens from this app (or device, if the device is in shared mode).
*/
mSingleAccountApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() {
@OverridepublicvoidonSignOut(){
mAccount = null;
updateUI();
showToastOnSignOut();
}
@OverridepublicvoidonError(@NonNull MsalException exception){
displayError(exception);
}
});
}
});
callGraphApiInteractiveButton.setOnClickListener(new View.OnClickListener() {
publicvoidonClick(View v){
if (mSingleAccountApp == null) {
return;
}
/**
* If acquireTokenSilent() returns an error that requires an interaction (MsalUiRequiredException),
* invoke acquireToken() to have the user resolve the interrupt interactively.
*
* Some example scenarios are
* - password change
* - the resource you're acquiring a token for has a stricter set of requirement than your Single Sign-On refresh token.
* - you're introducing a new scope which the user has never consented for.
*/
mSingleAccountApp.acquireToken(getActivity(), getScopes(), getAuthInteractiveCallback());
}
});
callGraphApiSilentButton.setOnClickListener(new View.OnClickListener() {
@OverridepublicvoidonClick(View v){
if (mSingleAccountApp == null) {
return;
}
/**
* Once you've signed the user in,
* you can perform acquireTokenSilent to obtain resources without interrupting the user.
*/
mSingleAccountApp.acquireTokenSilentAsync(getScopes(), mAccount.getAuthority(), getAuthSilentCallback());
}
});
}
@OverridepublicvoidonResume(){
super.onResume();
/**
* The account may have been removed from the device (if broker is in use).
*
* In shared device mode, the account might be signed in/out by other apps while this app is not in focus.
* Therefore, we want to update the account state by invoking loadAccount() here.
*/
loadAccount();
}
/**
* Extracts a scope array from a text field,
* i.e. from "User.Read User.ReadWrite" to ["user.read", "user.readwrite"]
*/private String[] getScopes() {
return scopeTextView.getText().toString().toLowerCase().split(" ");
}
/**
* Load the currently signed-in account, if there's any.
*/privatevoidloadAccount(){
if (mSingleAccountApp == null) {
return;
}
mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() {
@OverridepublicvoidonAccountLoaded(@Nullable IAccount activeAccount){
// You can use the account data to update your UI or your app database.
mAccount = activeAccount;
updateUI();
}
@OverridepublicvoidonAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount){
if (currentAccount == null) {
// Perform a cleanup task as the signed-in account changed.
showToastOnSignOut();
}
}
@OverridepublicvoidonError(@NonNull MsalException exception){
displayError(exception);
}
});
}
/**
* Callback used in for silent acquireToken calls.
*/private SilentAuthenticationCallback getAuthSilentCallback(){
returnnew SilentAuthenticationCallback() {
@OverridepublicvoidonSuccess(IAuthenticationResult authenticationResult){
Log.d(TAG, "Successfully authenticated");
/* Successfully got a token, use it to call a protected resource - MSGraph */
callGraphAPI(authenticationResult);
}
@OverridepublicvoidonError(MsalException exception){
/* Failed to acquireToken */
Log.d(TAG, "Authentication failed: " + exception.toString());
displayError(exception);
if (exception instanceof MsalClientException) {
/* Exception inside MSAL, more info inside MsalError.java */
} elseif (exception instanceof MsalServiceException) {
/* Exception when communicating with the STS, likely config issue */
} elseif (exception instanceof MsalUiRequiredException) {
/* Tokens expired or no session, retry with interactive */
}
}
};
}
/**
* Callback used for interactive request.
* If succeeds we use the access token to call the Microsoft Graph.
* Does not check cache.
*/private AuthenticationCallback getAuthInteractiveCallback(){
returnnew AuthenticationCallback() {
@OverridepublicvoidonSuccess(IAuthenticationResult authenticationResult){
/* Successfully got a token, use it to call a protected resource - MSGraph */
Log.d(TAG, "Successfully authenticated");
Log.d(TAG, "ID Token: " + authenticationResult.getAccount().getClaims().get("id_token"));
/* Update account */
mAccount = authenticationResult.getAccount();
updateUI();
/* call graph */
callGraphAPI(authenticationResult);
}
@OverridepublicvoidonError(MsalException exception){
/* Failed to acquireToken */
Log.d(TAG, "Authentication failed: " + exception.toString());
displayError(exception);
if (exception instanceof MsalClientException) {
/* Exception inside MSAL, more info inside MsalError.java */
} elseif (exception instanceof MsalServiceException) {
/* Exception when communicating with the STS, likely config issue */
}
}
@OverridepublicvoidonCancel(){
/* User canceled the authentication */
Log.d(TAG, "User cancelled login.");
}
};
}
/**
* Make an HTTP request to obtain MSGraph data
*/privatevoidcallGraphAPI(final IAuthenticationResult authenticationResult){
MSGraphRequestWrapper.callGraphAPIUsingVolley(
getContext(),
graphResourceTextView.getText().toString(),
authenticationResult.getAccessToken(),
new Response.Listener<JSONObject>() {
@OverridepublicvoidonResponse(JSONObject response){
/* Successfully called graph, process data and send to UI */
Log.d(TAG, "Response: " + response.toString());
displayGraphResult(response);
}
},
new Response.ErrorListener() {
@OverridepublicvoidonErrorResponse(VolleyError error){
Log.d(TAG, "Error: " + error.toString());
displayError(error);
}
});
}
//// Helper methods manage UI updates// ================================// displayGraphResult() - Display the graph response// displayError() - Display the graph response// updateSignedInUI() - Updates UI when the user is signed in// updateSignedOutUI() - Updates UI when app sign out succeeds///**
* Display the graph response
*/privatevoiddisplayGraphResult(@NonNull final JSONObject graphResponse){
logTextView.setText(graphResponse.toString());
}
/**
* Display the error message
*/privatevoiddisplayError(@NonNull final Exception exception){
logTextView.setText(exception.toString());
}
/**
* Updates UI based on the current account.
*/privatevoidupdateUI(){
if (mAccount != null) {
signInButton.setEnabled(false);
signOutButton.setEnabled(true);
callGraphApiInteractiveButton.setEnabled(true);
callGraphApiSilentButton.setEnabled(true);
currentUserTextView.setText(mAccount.getUsername());
} else {
signInButton.setEnabled(true);
signOutButton.setEnabled(false);
callGraphApiInteractiveButton.setEnabled(false);
callGraphApiSilentButton.setEnabled(false);
currentUserTextView.setText("None");
}
deviceModeTextView.setText(mSingleAccountApp.isSharedDevice() ? "Shared" : "Non-shared");
}
/**
* Updates UI when app sign out succeeds
*/privatevoidshowToastOnSignOut(){
final String signOutText = "Signed Out.";
currentUserTextView.setText("");
Toast.makeText(getContext(), signOutText, Toast.LENGTH_SHORT)
.show();
}
}
Öffnen Sie MainActivity.java, und ersetzen Sie den Code durch den folgenden Codeschnipsel, um die Benutzeroberfläche zu verwalten.
Stellen Sie sicher, dass Sie den Paketnamen so aktualisieren, dass er ihrem Android-Projektpaketnamen entspricht.
Layout
Ein Layout ist eine Datei, die die visuelle Struktur und Darstellung einer Benutzeroberfläche definiert und die Anordnung von UI-Komponenten angibt. Sie ist in XML geschrieben. Die folgenden XML-Beispiele werden bereitgestellt, wenn Sie Ihre Benutzeroberfläche aus diesem Tutorial modellieren möchten:
In app>src>main>res>layout>activity_main.xml. Ersetzen Sie den Inhalt von activity_main.xml durch den folgenden Codeschnipsel, um Schaltflächen und Textfelder anzuzeigen:
In app>src>main>res>layout>app_bar_main.xml. Wenn Sie in Ihrem Ordner nicht über app_bar_main.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
In app>src>main>res>layout>content_main.xml. Wenn Sie in Ihrem Ordner nicht über content_main.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
In app>src>main>res>layout>fragment_m_s_graph_request_wrapper.xml. Wenn Sie in Ihrem Ordner nicht über fragment_m_s_graph_request_wrapper.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
XML
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MSGraphRequestWrapper"><!-- TODO: Update blank fragment layout --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="@string/hello_blank_fragment" /></FrameLayout>
In app>src>main>res>layout>fragment_on_interaction_listener.xml. Wenn Sie in Ihrem Ordner nicht über fragment_on_interaction_listener.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
XML
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".OnFragmentInteractionListener"><!-- TODO: Update blank fragment layout --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:text="@string/hello_blank_fragment" /></FrameLayout>
In app>src>main>res>layout>fragment_single_account_mode.xml. Wenn Sie in Ihrem Ordner nicht über fragment_single_account_mode.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
XML
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".SingleAccountModeFragment"><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".SingleAccountModeFragment"><LinearLayoutandroid:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingBottom="@dimen/activity_vertical_margin"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="5dp"android:paddingBottom="5dp"android:weightSum="10"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:layout_gravity="center_vertical"android:textStyle="bold"android:text="Scope" /><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:orientation="vertical"android:layout_weight="7"><EditTextandroid:id="@+id/scope"android:layout_height="wrap_content"android:layout_width="match_parent"android:text="user.read"android:textSize="12sp" /><TextViewandroid:layout_height="wrap_content"android:layout_width="match_parent"android:paddingLeft="5dp"android:text="Type in scopes delimited by space"android:textSize="10sp" /></LinearLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="5dp"android:paddingBottom="5dp"android:weightSum="10"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:layout_gravity="center_vertical"android:textStyle="bold"android:text="MSGraph Resource URL" /><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:orientation="vertical"android:layout_weight="7"><EditTextandroid:id="@+id/msgraph_url"android:layout_height="wrap_content"android:layout_width="match_parent"android:textSize="12sp" /></LinearLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="5dp"android:paddingBottom="5dp"android:weightSum="10"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:textStyle="bold"android:text="Signed-in user" /><TextViewandroid:id="@+id/current_user"android:layout_width="0dp"android:layout_height="wrap_content"android:paddingLeft="5dp"android:layout_weight="7"android:text="None" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="5dp"android:paddingBottom="5dp"android:weightSum="10"><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="3"android:textStyle="bold"android:text="Device mode" /><TextViewandroid:id="@+id/device_mode"android:layout_width="0dp"android:layout_height="wrap_content"android:paddingLeft="5dp"android:layout_weight="7"android:text="None" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:paddingTop="5dp"android:paddingBottom="5dp"android:weightSum="10"><Buttonandroid:id="@+id/btn_signIn"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="5"android:gravity="center"android:text="Sign In"/><Buttonandroid:id="@+id/btn_removeAccount"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="5"android:gravity="center"android:text="Sign Out"android:enabled="false"/></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><Buttonandroid:id="@+id/btn_callGraphInteractively"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="5"android:text="Get Graph Data Interactively"android:enabled="false"/><Buttonandroid:id="@+id/btn_callGraphSilently"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="5"android:text="Get Graph Data Silently"android:enabled="false"/></LinearLayout><TextViewandroid: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></LinearLayout></FrameLayout>
In app>src>main>res>layout>nav_header_main.xml. Wenn Sie in Ihrem Ordner nicht über nav_header_main.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
Im app>src>main>res>-Menü>activity_main_drawer.xml. Wenn Sie in Ihrem Ordner nicht über activity_main_drawer.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
In den app>src>main>res>-Werten>strings.xml. Ersetzen Sie den Inhalt von strings.xml durch den folgenden Codeschnipsel:
XML
<resources><stringname="app_name">MSALAndroidapp</string><stringname="action_settings">Settings</string><!-- Strings used for fragments for navigation --><stringname="first_fragment_label">First Fragment</string><stringname="second_fragment_label">Second Fragment</string><stringname="nav_header_desc">Navigation header</string><stringname="navigation_drawer_open">Open navigation drawer</string><stringname="navigation_drawer_close">Close navigation drawer</string><stringname="next">Next</string><stringname="previous">Previous</string><stringname="hello_first_fragment">Hello first fragment</string><stringname="hello_second_fragment">Hello second fragment. Arg: %1$s</string><!-- TODO: Remove or change this placeholder text --><stringname="hello_blank_fragment">Hello blank fragment</string></resources>
In app>src>main>res>values>styles.xml. Wenn Sie in Ihrem Ordner nicht über styles.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
XML
<resources><!-- Base application theme. --><stylename="AppTheme"parent="Theme.AppCompat.Light.DarkActionBar"><!-- Customize your theme here. --><itemname="colorPrimary">@color/colorPrimary</item><itemname="colorPrimaryDark">@color/colorPrimaryDark</item><itemname="colorAccent">@color/colorAccent</item></style><stylename="AppTheme.NoActionBar"><itemname="windowActionBar">false</item><itemname="windowNoTitle">true</item></style><stylename="AppTheme.AppBarOverlay"parent="ThemeOverlay.AppCompat.Dark.ActionBar" /><stylename="AppTheme.PopupOverlay"parent="ThemeOverlay.AppCompat.Light" /></resources>
In app>src>main>res>values>themes.xml. Ersetzen Sie den Inhalt von themes.xml durch den folgenden Codeschnipsel:
XML
<resourcesxmlns:tools="http://schemas.android.com/tools"><!-- Base application theme. --><stylename="Theme.MSALAndroidapp"parent="Theme.MaterialComponents.DayNight.DarkActionBar"><!-- Primary brand color. --><itemname="colorPrimary">@color/purple_500</item><itemname="colorPrimaryVariant">@color/purple_700</item><itemname="colorOnPrimary">@color/white</item><!-- Secondary brand color. --><itemname="colorSecondary">@color/teal_200</item><itemname="colorSecondaryVariant">@color/teal_700</item><itemname="colorOnSecondary">@color/black</item><!-- Status bar color. --><itemname="android:statusBarColor"tools:targetApi="21">?attr/colorPrimaryVariant</item><!-- Customize your theme here. --></style><stylename="Theme.MSALAndroidapp.NoActionBar"><itemname="windowActionBar">false</item><itemname="windowNoTitle">true</item></style><stylename="Theme.MSALAndroidapp.AppBarOverlay"parent="ThemeOverlay.AppCompat.Dark.ActionBar" /><stylename="Theme.MSALAndroidapp.PopupOverlay"parent="ThemeOverlay.AppCompat.Light" /></resources>
In app>src>main>res>drawable>ic_single_account_24dp.xml. Wenn Sie in Ihrem Ordner nicht über ic_single_account_24dp.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
In app>src>main>res>drawable>side_nav_bar.xml. Wenn Sie in Ihrem Ordner nicht über side_nav_bar.xml verfügen, erstellen Sie den folgenden Codeschnipsel, und fügen Sie ihn hinzu:
In app>src>main>res>drawable. Fügen Sie dem Ordner ein PNG-Microsoft-Logo mit dem Namen „microsoft_logo.png“ hinzu.
Wenn Sie Ihre Benutzeroberfläche in XML deklarieren, können Sie die Darstellung Ihrer App von dem Code, der ihr Verhalten steuert, trennen. Weitere Informationen zum Android-Layout finden Sie unter Layouts.
Testen Ihrer App
Lokales Ausführen
Erstellen Sie die App, und stellen Sie sie auf einem Testgerät oder in einem Emulator bereit. Sie sollten sich anmelden und Token für Microsoft Entra ID oder persönliche Microsoft-Konten abrufen können.
Nach der Anmeldung zeigt die App die vom Microsoft Graph-Endpunkt /me zurückgegebenen Daten an.
Zustimmung
Wenn sich ein Benutzer zum ersten Mal bei Ihrer App anmeldet, wird er von Microsoft Identity aufgefordert, den angeforderten Berechtigungen zuzustimmen. Bei einigen Microsoft Entra-Mandanten ist die Benutzereinwilligung deaktiviert, sodass Administratoren im Namen aller Benutzer einwilligen müssen. Um dieses Szenario zu unterstützen, müssen Sie entweder einen eigenen Mandanten anlegen oder die Einwilligung des Administrators einholen.
Wenn Sie Hilfe benötigen, ein Problem melden möchten oder sich über Ihre Supportoptionen informieren möchten, finden Sie weitere Informationen unter Hilfe und Support für Entwickler.
Hier erfahren Sie, wie Sie Benutzer mit Microsoft Entra ID authentifizieren und in einer Java-Web-App mithilfe der Microsoft-Authentifizierungsbibliothek autorisierten Zugriff auf Daten erhalten.