Jesteś programistą .NET, który zawsze chciał stworzyć aplikację mobilną? A może próbowałeś budować natywne aplikacje mobilne z Androidem lub iOS, ale nie podobały Ci się te języki? Cóż, masz szczęście! Świat .NET został pobłogosławiony przez Xamarin; zestaw narzędzi, który pozwala budować aplikacje mobilne dla Androida, iOS i Windows w Visual Studio.
Xamarin ma dwa główne smaki: Xamarin platform (Xamarin.iOS i Xamarin.Android) oraz Xamarin.Forms. Dzięki Xamarin.Forms zdecydowana większość logiki biznesowej i interfejsu użytkownika może być napisana w ramach jednego wspólnego projektu, który produkuje w pełni funkcjonalne aplikacje na wszystkie 3 systemy operacyjne iOS, Android i Windows (UWP). Platforma Xamarin, z drugiej strony, jest bardzo specyficzna dla platformy i jest bardziej zbliżona do pisania aplikacji natywnych, ale w C#.
W tym poradniku, będę przyglądał się bliżej platformie Xamarin i zestawowi narzędzi dla systemu operacyjnego Android, znanemu jako Xamarin.Android. Ogólnym celem jest umożliwienie ci stworzenia prostej natywnej aplikacji na Androida z podstawowym uwierzytelnianiem użytkownika.
- Set Up Visual Studio and Your Environment
- Verify Your Android Environment in Visual Studio
- Utwórz aplikację Xamarin
- Zapoznaj się z projektem Xamarin
- Add User Authentication to Xamarin with OpenID Connect
- Ustaw swoją aplikację Okta
- Utwórz dostawcę uwierzytelniania
- Implement Authentication into Your Xamarin Interface
- Run Your Android App
- Learn More About Xamarin, OpenID Connect, and Secure Authentication
Set Up Visual Studio and Your Environment
Aby podążać dalej, będziesz potrzebował kopii Visual Studio, plus obciążenie 'Mobile development with .NET’. Możesz albo włączyć tę funkcję z pierwszej instalacji Visual Studio lub uzyskać do niej dostęp z pozycji menu 'Tools -> Get Tools and Features…’:
Podczas testowania i uruchamiania aplikacji masz do wyboru zrobienie tego z emulatorem Androida działającym na twojej maszynie deweloperskiej, lub przez bezpośrednie połączenie z istniejącym urządzeniem Android. Nie ma tutaj właściwej opcji, a różni deweloperzy preferują różne czynniki formalne. Jeśli wybierzesz tę pierwszą opcję, będziesz musiał upewnić się, że po wybraniu obciążenia roboczego, w prawym panelu („Szczegóły instalacji”) zaznaczone są pola wyboru dla Intel Hardware Accelerated Execution Manager i Google Android Emulator (jak widać powyżej).
Verify Your Android Environment in Visual Studio
Aby sprawdzić czy wszystko zainstalowało się prawidłowo i zostało poprawnie skonfigurowane, przejdź do 'Tools -> Options -> Xamarin -> Android Settings’ i sprawdź czy ścieżki Java Development Kit Location i Android SDK Location są poprawne (tj.
Jeśli brakuje którejś z nich, będziesz musiał ręcznie zainstalować odpowiednio Java Development Kit lub Android SDK.
Utwórz aplikację Xamarin
Zacznij od utworzenia nowego projektu i wybierz szablon główny 'Android App (Xamarin)’ (znajdziesz go w menu Android). Na następnej stronie będziesz chciał wybrać opcję 'Single View App’, ponieważ jest to świetny szablon startowy do pracy.
W odniesieniu do minimalnej wersji Androida, jest to coś w dół do osobistego wyboru jako deweloper. Istnieje kompromis pomiędzy możliwością dostępu do najnowszych i największych funkcji API w nowszych wersjach i wspieraniem klientów, którzy mają starsze wersje. Aby pomóc Ci podjąć tę decyzję, Google publikuje dane dotyczące dystrybucji wersji platformy, które zbierają jako część ich Distribution dashboard na dość regularnej kadencji. Moja osobista preferencja jest między 5.0 lub 6.0 w zależności od tego, czy jest to aplikacja do publicznej konsumpcji lub zleconej aplikacji dla telefonów firmowych tylko (tj. będą one prawdopodobnie mają najnowsze aktualizacje); w tym przykładzie poszedłem z tym ostatnim. Uwaga ta wersja różni się od docelowej wersji Androida i to zawsze powinno być ustawione na najnowszą wydaną wersję SDK, jak cokolwiek mniej nie zostanie zaakceptowane do sklepu Google Play.
Po utworzeniu tego wszystko, co pozostało to zaimportować wymagane pakiety NuGet. Do tego tutoriala będziesz potrzebował:
- Xamarin.OpenId.AppAuth.Android – Do uwierzytelniania użytkownika będziesz używał standardu OpenID Connect (rozszerzenie OAuth 2.0). Najprostszym sposobem na wdrożenie kodu klienta, który przestrzega tej specyfikacji, jest użycie klienta AppAuth SDK dla Androida, a Xamarin przeniósł pakiet tej funkcjonalności dostępny do użycia.
- System.IdentityModel.Tokens.Jwt – Metoda uwierzytelniania tutaj używa JSON Web Tokens. Aby wyodrębnić dane potrzebne z tych tokenów będziesz potrzebował tego pakietu.
Dodatkowo, będziesz potrzebował tych dwóch pakietów, ponieważ są one zależne od AppAuth:
- Xamarin.Android.Support.v4
- Xamarin.Android.Support.CustomTabs
Zapoznaj się z projektem Xamarin
Jeśli nigdy wcześniej nie pracowałeś z Androidem, jedną z kluczowych zasad, którą musisz sobie przyswoić jest koncepcja aktywności. Aktywności są komponentami używanymi do wyświetlania interfejsu użytkownika; w najbardziej podstawowej formie, można myśleć o aktywnościach jak o stronach w aplikacji, pomiędzy którymi użytkownik może się poruszać. Aktywność w kodzie jest reprezentowana przez klasę, jednak podobnie jak w przypadku strony w ASP.NET można (i prawie zawsze się to robi) powiązać z nią plik układu oparty na XML (plik .axml), aby uzyskać wyświetlany projekt. Wszystkie nowe projekty tworzą plik 'MainActivity.cs’ i 'activity_main.axml’ jako pierwszą aktywność (tj. stronę) uruchamianą po otwarciu aplikacji. Może to być zmienione na dowolną inną aktywność poprzez wykorzystanie właściwości MainLauncher = true
w atrybucie Activity
klasy.
Zasoby są zaprojektowane tak, aby były obsługiwane w ich własnym katalogu i podążają za nieco ścisłą konwencją nazewnictwa. Zdecydowanie zalecam przechowywanie jak największej ilości zasobów w tym katalogu, ponieważ upraszcza to ponowne użycie tych zmiennych w bieżącym rozwoju. W katalogu 'values’ w katalogu Resources znajdziesz pliki o określonym przeznaczeniu:
- Strings.xml – Zawiera wszystkie zmienne skierowane do użytkownika. Ten plik jest szczególnie ważny, ponieważ umożliwia lokalizację ciągów znaków dla globalnej publiczności.
- Styles.xml – Tutaj znajdziesz atrybuty do stylizacji obiektów projektu; pomyśl o nim jak o pliku CSS.
- Colors.xml – Miejsce do przechowywania odniesień do kolorów, których najczęściej używasz jako części stylizacji.
- Dimens.xml – Jak sama nazwa może wskazywać, definiuje określone wymiary układu aplikacji.
Inne ważne katalogi nie stosują żadnej konwencji nazewnictwa plików, ale muszą zawierać określone typy plików:
- Layout – Miejsce przechowywania plików .axml. Te pliki definiują pełne układy aktywności, komponenty układów, które są generowane i wypełniane programowo, układy okien dialogowych (alertów) itp.
- Menu – miejsce, w którym znajdują się definicje menu i ich elementów. Są to pliki .xml, które mają
menu
jako element główny orazitem
elementy potomne, które mogą być zgrupowane razem z elementamigroup
. Najczęściej spotykane menu to menu przelewu (z przycisku z trzema pionowymi kropkami) lub menu nawigacyjne (z przycisku 'hamburger’). - Mipmap – Gdzie chcesz zdefiniować obrazy, które muszą być skalowane w zależności od gęstości ekranu, tj. te, do których odwołują się inne aplikacje, a nie są używane wewnętrznie. Ikona aplikacji jest najczęstszą zawartością, którą można umieścić w katalogach mipmap.
- Drawable – Nie jest standardem w domyślnym szablonie projektu, ale może być tworzona samodzielnie. To tutaj przechowywane są obrazy/rysunki, które mają być używane w aplikacji, np. splash screen, niestandardowe projekty paska postępu, ikony używane w aplikacji, itp.
Na koniec, jeśli masz jakieś pliki z surową zawartością, które chcesz użyć jako część aplikacji (np. tekst lub plik czcionki), to katalog Assets jest miejscem, gdzie je umieścić. Jako Assets pliki te zostaną rozmieszczone w twojej aplikacji, abyś miał do nich dostęp za pomocą Asset Manager.
Aby dowiedzieć się więcej o Assets i Resources oraz jak ich używać, w każdym katalogu nowo utworzonego projektu znajdują się poręczne pliki tekstowe 'About’.
Add User Authentication to Xamarin with OpenID Connect
Większość aplikacji w dzisiejszych czasach wymaga jakiejś formy identyfikacji użytkownika, aby umożliwić deweloperowi oferowanie doświadczeń dostosowanych do potrzeb i z kolei umożliwić użytkownikowi przechowywanie danych na różnych urządzeniach/instalacjach. Dodatkowo istnieje kwestia kontroli dostępu, która może być przydatna do autoryzacji podzbioru użytkowników do dodatkowych funkcji. Oczywiście może to być dość pracochłonne zadanie, zwłaszcza jeśli zajmujesz się tworzeniem aplikacji i dla każdej z nich będziesz potrzebował nowego systemu. Na szczęście z Oktą możesz skonfigurować aplikację w zaledwie kilka minut, a wtedy cała ciężka praca zostanie wykonana za Ciebie! Usługa Okta jest zgodna z OAuth 2.0 i jest certyfikowanym dostawcą OpenID Connect, a więc doskonale współpracuje z klientem AppAuth SDK dla wszystkich Twoich potrzeb uwierzytelniania i autoryzacji.
Ustaw swoją aplikację Okta
Po pierwsze, powinieneś ustawić nową aplikację na swoim koncie Okta dla tego projektu. Jeśli jeszcze go nie masz, naprawdę łatwo jest utworzyć nowe, bezterminowe konto dewelopera.
Jak już to zrobisz i zalogujesz się do pulpitu dewelopera, zanotuj adres URL Org, ponieważ będziemy go potrzebować później:
Z pulpitu przejdź do zakładki „Aplikacje”, a stamtąd „Dodaj aplikację”. Tworzysz natywną aplikację na Androida, więc najlepiej wybrać szablon platformy 'Native’.
Z tej strony dodaj nazwę dla swojej aplikacji i pozostaw wszystko inne jako domyślne. Po zapisaniu zanotuj swoje URI przekierowania logowania i ID klienta, ponieważ będziesz ich potrzebował w następnej kolejności.
Abyś mógł w przyszłości z łatwością używać tych trzech wartości z Okta, zalecałbym umieszczenie ich w ich własnej klasie statycznej w nowym katalogu dla logiki uwierzytelniania:
public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}
Utwórz dostawcę uwierzytelniania
Pozbądźmy się już gotowego kodu. Musisz skonfigurować aplikację, aby poinformować ją o schemacie URI przekierowania. Schemat jest twoim URI przekierowania logowania (w standardowej formie odwróconej nazwy domeny) bez ścieżki. Na przykład, na powyższym zrzucie ekranu mój schemat byłby 'com.oktapreview.dev-123456′.
Najprostszym sposobem na zrobienie tego jest wstawienie poniższego fragmentu filtru intencji do pliku AndroidManifest.xml
w folderze Properties
twojego rozwiązania. Dodaj następujący XML wewnątrz tagu Application
i zmień wartość schematu na własną:
<activity android:name="net.openid.appauth.RedirectUriReceiverActivity"> <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="{yourRedirectRriScheme}" /> </intent-filter></activity>
Musisz również zdefiniować model dla wyniku autoryzacji za pomocą prostej klasy. Chociaż nie będę używał wszystkich wartości, które napisałem poniżej w kodzie, pokażę jak je wypełnić, abyś mógł z nich później korzystać. Ponieważ jest to część modelu twojej aplikacji, stwórz folder o nazwie Models
i dodaj do niego klasę AuthorizationResult.cs
. Następnie dodaj następujący kod:
public class AuthorizationResult{ public string AccessToken { get; set; } public string IdentityToken { get; set; } public bool IsAuthorized { get; set; } public string RefreshToken { get; set; } public string Scope { get; set; }}
Android nie posiada globalnego stanu, z którym mógłbyś pracować, więc jeśli chcesz przekazywać proste wartości pomiędzy aktywnościami, najlepszym sposobem na zrobienie tego jest funkcjonalność Extras
na obiekcie Intent
. Obiekt Intent
jest predefiniowaną klasą w Androidzie i kolejną podstawową koncepcją do zrozumienia. Jest to abstrakcja operacji, która ma być wykonana (tj. Twoje „intencje”), a aby przejść do innej aktywności, musisz utworzyć instancję tej klasy z aktywnością, do której „zamierzasz” przejść. Właściwości Extras na obiekcie Intent
są w efekcie po prostu słownikiem kluczy do wartości obiektu i są dostępne przez Put
i typowane Get
metody.
Podczas gdy te metody utrzymują użycie stosunkowo jasne i łatwe, osobiście lubię utrzymywać cały dostęp do nich wewnątrz ich własnej klasy (a dokładniej klasy rozszerzeń), aby utrzymać lepszą separację obaw. Jest to niezwykle użyteczne, ponieważ nie musisz mieć dostępu do kluczy w różnych klasach i możesz zapewnić bezpieczeństwo typu podczas wprowadzania i uzyskiwania tych wartości. W twoim dostawcy autoryzacji będziesz chciał: przechowywać AuthState
, być w stanie sprawdzić czy jest tam i zwrócić go, jeśli jest. Utwórz nowy folder o nazwie Extensions
w korzeniu rozwiązania. Następnie dodaj nową klasę o nazwie IntentExtensions.cs
. Utwórz klasy public
i static
, a następnie dodaj następujący kod wewnątrz klasy:
public const string AuthStateKey = "authState";public static string GetAuthStateExtra(this Intent intent){ return intent.GetStringExtra(AuthStateKey);}public static bool HasAuthStateExtra(this Intent intent){ return intent.HasExtra(AuthStateKey);}public static void PutAuthStateExtra(this Intent intent, AuthState authState){ intent.PutExtra(AuthStateKey, authState.JsonSerializeString());}public static bool TryGetAuthStateFromExtra(this Intent intent, out AuthState authState){ authState = null; if (!intent.HasAuthStateExtra()) { return false; } try { authState = AuthState.JsonDeserialize(intent.GetAuthStateExtra()); } catch (JSONException) { return false; } return authState != null;}
Teraz nadszedł czas na zdefiniowanie dostawcy autoryzacji, AuthorizationProvider.cs
w folderze Authentication
, który utworzyłeś wcześniej dla klasy Configuration.cs
. Po pierwsze, usuń wszystkie using
deklaracje wewnątrz nowo utworzonej klasy, następnie zadeklaruj konfigurację jako static readonly
zmienne:
private static readonly Uri ConfigurationEndpoint = Uri.Parse($"{Configuration.OrgUrl}/oauth2/default/.well-known/openid-configuration");private static readonly string Scopes = new { "openid", "profile", "email", "offline_access" };
Konfiguracyjny punkt końcowy jest standardem w OpenID jako punkt końcowy odkrycia, aby znaleźć wszystko, co jest obsługiwane. Uwaga tutaj napisałem, że jest to przy użyciu „domyślnej” nazwy dostawcy. Jeśli masz innego dostawcę, będziesz chciał to tutaj zmienić. Zauważ również, że używa to klasy Uri w wersji Android.Net
, a nie System
– aby to działało, musisz dodać tę pierwszą do swojego użycia lub w pełni zakwalifikować typ. Zmienna Scopes, tak jak w każdym innym systemie OpenID, definiuje do czego mamy dostęp.
Następnie należy zadeklarować zmienne członkowskie:
private AuthorizationRequest authorizationRequest;private PendingIntent completedIntent;private AuthState authorizationState;private readonly AuthorizationService authorizationService;private readonly Context context;private readonly TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
Szybkie wyjaśnienie każdej z nich:
- Żądanie autoryzacji i zakończony zamiar są parametrami stworzonymi do użycia w wywołaniu autoryzacji. Zapisałem je tutaj jako zmienne globalne, aby zminimalizować ilość przekazywania parametrów do różnych metod.
- Zmienna authorizationState, zgodnie z nazwą, określa bieżący stan autoryzacji.
- Zmienna authorizationService zawiera instancję usługi autoryzacji.
- Zmienna context należy do aktywności wywołującej, więc można się do niej odwoływać w razie potrzeby.
- Wreszcie, taskCompletionSource umożliwia asynchroniczne wykonywanie wszystkich tych wywołań, a następnie zwrócenie ich po zakończeniu.
Teraz powinieneś zdefiniować wartości tych zmiennych tylko do odczytu w swoim konstruktorze i zadeklarować metody publiczne, które Twój kod będzie wywoływał:
public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}
Metoda SignInAsync jest, jak mogłeś się domyślić, asynchroniczną metodą logowania użytkownika. Zwraca ona klasę AuthorizationResult
, którą napisałeś wcześniej. NotifyCallback
z drugiej strony jest dla aktywności wywołującej, po powrocie z zewnętrznej strony logowania, aby zadzwonić z powrotem do dostawcy autoryzacji i dać mu znać, że to już się stało. Metoda logowania została podzielona na wiele podprogramów i wygląda tak:
AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();
W tym zdefiniowałeś konfigurację usługi, zbudowałeś żądanie autoryzacji i intencję wywołania po zakończeniu autoryzacji, a następnie oczekujesz na żądanie autoryzacji. Aby zbudować żądanie autoryzacji jest w następujący sposób:
private void BuildAuthorizationRequest(AuthorizationServiceConfiguration serviceConfiguration){ AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder( serviceConfiguration, Configuration.ClientId, ResponseTypeValues.Code, Uri.Parse(Configuration.LoginRedirectUri)); builder.SetScope(string.Join(" ", Scopes)); authorizationRequest = builder.Build();}
Zadaniem tej metody jest abstrahowanie od AuthorizationRequest.Builder
pracy i tworzenie żądania. Następnie musisz zbudować Intent
po zakończeniu operacji:
private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}
Zamiarem, który chcesz tutaj wykonać, jest powrót do twojego MainActivity
z dołączonym nowym AuthState. Ostatnim elementem tego przepływu jest zajęcie się wykonaniem żądania:
private async Task<AuthorizationResult> RequestAuthorization(){ authorizationService.PerformAuthorizationRequest(authorizationRequest, completedIntent); await taskCompletionSource.Task; return new AuthorizationResult() { AccessToken = authorizationState?.AccessToken, IdentityToken = authorizationState?.IdToken, IsAuthorized = authorizationState?.IsAuthorized ?? false, RefreshToken = authorizationState?.RefreshToken, Scope = authorizationState?.Scope };}
Jako że PerformAuthorizationRequest
jest synchroniczne i zwraca void, kod oczekuje na członka taskCompletionSource
, wiedząc, że będzie on ustawiony tylko wtedy, gdy odpowiedź zostanie pobrana. W tym samym momencie wiesz, że stan autoryzacji zostanie wypełniony (jeśli wszystko się powiedzie), a więc możesz zwrócić ich wartości jako część AuthorizationResult.
Druga metoda publiczna NotifyCallback
, jak już wspomniałem, jest tym, co chcesz, aby klasa MainActivity
oddzwoniła, gdy twój powyższy completedIntent
zostanie uruchomiony. W tej metodzie chcesz zweryfikować odpowiedź, odpowiednio zaktualizować stan, a jeśli się powiedzie, wykonać żądanie wymiany tokena:
if (!intent.TryGetAuthStateFromExtra(out authorizationState)){ taskCompletionSource.SetResult(false); return;}AuthorizationResponse authorizationResponse = AuthorizationResponse.FromIntent(intent);AuthorizationException authorizationException = AuthorizationException.FromIntent(intent);authorizationState.Update(authorizationResponse, authorizationException);if (authorizationException != null){ taskCompletionSource.SetResult(false); return;}authorizationService.PerformTokenRequest(authorizationResponse.CreateTokenExchangeRequest(), ReceivedTokenResponse);
Tutaj możesz zobaczyć, że w przypadkach niepowodzenia ustawiłem wynik taskCompletionSource na false, a to odblokuje powyższą metodę RequestAuthorization
. Ponadto, metoda PerformTokenRequest
przyjmuje delegata, ReceivedTokenResponse
, który zostanie uruchomiony po jej zakończeniu. Metoda ta wygląda następująco:
private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}
W tym momencie powinieneś mieć wszystkie dane autoryzacyjne, których potrzebujesz, a więc możesz odpowiednio zaktualizować stan (gdzie znajdziesz wartości do zwrócenia z metody sign in) i ustawić wynik, aby odblokować zadanie taskCompletionSource
.
Implement Authentication into Your Xamarin Interface
As a clean-up if you so wish, feel free to remove all references to the 'Floating Action Bar’ (also written as 'FAB’) within the main activity class/axml files as they are unnecessary bloat at this stage.
Aby umożliwić użytkownikowi zalogowanie się, musisz teraz zaimplementować tę funkcjonalność w UI. Biorąc pod uwagę domyślny projekt, najbardziej intuicyjnym sposobem na zrobienie tego byłoby dodanie elementu do menu przepełnienia w prawym górnym rogu. Możesz to zrobić edytując plik menu_main.xml
w folderze 'Resources -> menu’ i dodając ten element jako dziecko tagu menu
:
<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />
Z tym kodem utworzyłeś nową opcję z tytułem, który ma być zdefiniowany w pliku string resources. Jak wspomniano wcześniej w systemie Android najlepszą praktyką jest umieszczanie wszystkich tekstów skierowanych do użytkownika w pliku string resources. Aby zadeklarować te dane, edytuj plik strings.xml
w folderze 'Resources -> values’ i dodaj te linie:
<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>
Nie tylko zadeklarowałem tutaj ciąg dla przycisku 'Sign In’, ale również dodałem powyżej ciąg dla wiadomości powitalnej dla użytkownika po zalogowaniu. Odpowiednikiem kodu C# jest this string would be
„Hi, {0}!”`, gdzie placeholder jest typu string.
Zauważ, że przy wszystkich aktualizacjach tych plików opartych na zasobach, klasa Resource.designer.cs zostanie automatycznie zregenerowana z nowymi identyfikatorami dla każdego utworzonego obiektu, do którego można się odwoływać w kodzie. Jeśli nie działa to dla konkretnego pliku, zaznacz go w Solution Explorerze i spójrz na okno Properties. Upewnij się, że właściwość CustomTool
jest ustawiona na wartość MSBuild:UpdateGeneratedFiles
, ponieważ prawdopodobnie jej brakuje i uniemożliwia ona rozpoznanie pliku projektanta jako zasobu.
Następnie dodaj ProgressBar
do istniejącego pliku układu activity_main.axml
:
<ProgressBar android:id="@+id/signin_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingBottom="12dp" android:visibility="gone" />
Ten ProgressBar
(lub spinner, w zależności od przypadku), ma identyfikator, do którego można się odwoływać za pomocą kodu, i jest ustawiony tak, aby znajdował się na środku ekranu. Widoczność jest na razie ustawiona na nieobecną, ale po uruchomieniu kodu autoryzacji możesz ustawić ją na widoczną i poinformować użytkownika, że aplikacja jest zajęta.
Teraz masz przycisk do otwierania uwierzytelniania i spinner postępu, aby poinformować użytkownika, że aplikacja jest zajęta, nadszedł czas, aby ich użyć. W swojej klasie MainActivity
dodaj następującą właściwość do atrybutu Activity
(nad nagłówkiem klasy):
LaunchMode = LaunchMode.SingleTask
Ta właściwość zapewnia, że istnieje tylko jedna instancja klasy MainActivity
i nie będziesz ciągle otwierać nowych. Kiedy już to zrobisz, dodaj statyczną zmienną członkowską dla AuthorizationProvder
, którą napisałeś powyżej i utwórz jej instancję w istniejącej nadpisanej metodzie OnCreate
. Zauważ, że powinno to być zrobione po istniejącym kodzie wewnątrz metody:
private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}
Następnie, nadpisz metodę OnNewIntent
. Ma to na celu to, że gdy zostanie utworzony nowy int tej klasy (czyli gdy powróci zewnętrzne okno logowania), wywołasz metodę NotifyCallback
z metody AuthorizationProvider
. Zawarte jest w tym również szybkie sprawdzenie, aby upewnić się, że jest to oczekiwany przepływ:
protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}
Teraz dodaj kod za dodanym elementem menu. W istniejącej nadpisce metody OnOptionsItemSelected
dodaj instrukcję if
z wywołaniem nowej metody, która obsłuży proces logowania w następujący sposób:
if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}
Ta nowa metoda zacznie od uwidocznienia ProgressBar
, którą dodałeś chwilę temu; aby pobrać dowolny komponent z pliku układu, użyj metody generycznej FindViewById
i podaj identyfikator komponentu jako jej argument. Następnie wykonaj wywołanie metody SignInAsync
i poczekaj na jej wynik. Po powrocie wywołania wynik jest weryfikowany jako autoryzowany. Jeśli autoryzacja nie powiodła się z jakiegokolwiek powodu, pojawi się okno dialogowe błędu, a spinner postępu ponownie zniknie. Zostawię na razie wyszczególnienie przypadku sukcesu, ponieważ nadal potrzebujesz gdzieś pójść w tym przypadku:
private async void OnSignInAttempted(){ ProgressBar signInProgress = FindViewById<ProgressBar>(Resource.Id.signin_progress); signInProgress.Visibility = ViewStates.Visible; AuthorizationResult authorizationResult = await authorizationProvider.SignInAsync(); if (!string.IsNullOrWhiteSpace(authorizationResult.AccessToken) && authorizationResult.IsAuthorized) { // Placeholder: Success case } else { // Display an error to the user AlertDialog authorizationErrorDialog = new AlertDialog.Builder(this) .SetTitle("Error!") .SetMessage("We were unable to authorize you.") .Create(); authorizationErrorDialog.Show(); signInProgress.Visibility = ViewStates.Gone; }}
Gdy użytkownik jest uwierzytelniony, powinieneś przekierować go na następną stronę twojego doświadczenia. Jeśli pamiętasz wcześniej, każda strona jest reprezentowana przez aktywność, więc musisz teraz utworzyć nową.
Na początek, w folderze 'Resources -> layout’ musisz utworzyć plik nowego layoutu aktywności „activity_dashboard.axml”. Najprostszym sposobem jest wybranie opcji New Item… z menu kontekstowego i wybranie szablonu 'Android Layout’. W nowym pliku układu dodaj prosty komponent TextView, który będzie wyświetlał tekst, jak poniżej:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:id="@+id/welcome_message" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /></LinearLayout>
W tym snippecie masz komponent TextView
o referencyjnym ID, który jest wyśrodkowany na środku strony, i który będzie wyświetlał wiadomość powitalną. Następnie należy utworzyć odpowiednią klasę aktywności 'DashboardActivity’ za pomocą opcji 'New Item…’ w menu kontekstowym projektu w eksploratorze rozwiązań i wybierając szablon 'Activity’. Aby powiązać tę klasę z jej plikiem layoutu, należy wywołać funkcję SetContentView w wygenerowanej OnCreate()
metodzie (pod dziedziczonym wywołaniem metody bazowej):
SetContentView(Resource.Layout.activity_dashboard);
Aby spersonalizować wiadomość powitalną, należy przekazać do nowej aktywności nazwę użytkownika. Jeśli pamiętasz wcześniej, najlepszym sposobem na zrobienie tego było użycie Extras w intrze, a ty stworzyłeś klasę extensions, aby to obsłużyć. Tak jak poprzednio, dodaj nowe metody 'Put’ i 'Get’ dodatku 'name’ w pliku IntentExtensions.cs
, który utworzyłeś powyżej:
private const string NameKey = "Name";public static void PutNameExtra(this Intent intent, string name){ intent.PutExtra(NameKey, name);}public static string GetNameExtra(this Intent intent){ return intent.GetStringExtra(NameKey);}
Teraz używając tej rozszerzonej funkcjonalności, po wywołaniu SetContentView
, które wykonałeś w metodzie OnCreate()
, pobierz nazwę użytkownika i ustaw odpowiednio tekst komponentu TextView
:
string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);
W tym fragmencie, po pobraniu instancji TextView
jej wartość jest ustawiana na Twoją wiadomość powitalną, której treść jest tworzona przy użyciu Android Resources odpowiednika string.Format()
.
Dzięki temu Twoja aktywność na desce rozdzielczej jest kompletna i teraz musisz się do niej odwołać. W placeholderze dla przypadku sukcesu, który pozostawiłem otwarty z metody OnSignInAttempted
, możesz to osiągnąć, dodając następujący kod:
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();JwtSecurityToken jsonToken = tokenHandler.ReadJwtToken(authorizationResult.IdentityToken);IEnumerable<Claim> claims = jsonToken?.Payload?.Claims;string name = claims?.FirstOrDefault(x => x.Type == "name")?.Value;Intent dashboardIntent = new Intent(this, typeof(DashboardActivity));dashboardIntent.PutNameExtra(name);StartActivity(dashboardIntent);Finish();
Pierwszy blok odczytuje token i pobiera nazwę użytkownika (jeśli istnieje). W drugim tworzony jest nowy Intent
dla aktywności dashboardu, nazwa użytkownika jest przechowywana w tym Intent
przy użyciu wyżej zdefiniowanej metody rozszerzenia, a następnie aktywność jest uruchamiana (tj. nawigowana do). Aby uniemożliwić użytkownikowi ponowne przejście do tej strony, kod kończy się wywołaniem metody Finish()
.
Run Your Android App
Teraz nadszedł czas na uruchomienie aplikacji na wybranym urządzeniu!
Jeśli debugujesz używając emulatora, powinno to być tak proste, jak naciśnięcie F5, które najpierw otworzy i załaduje emulator, a następnie kod zostanie do niego wdrożony. Jako notatka boczna nie musisz zamykać emulatora pomiędzy próbami uruchomienia/debugowania, ponieważ wymaga on tylko ponownego rozmieszczenia aplikacji.
Jeśli debugujesz używając urządzenia, które nie było używane do tego celu wcześniej, będziesz musiał najpierw ustawić urządzenie. Zrób to włączając opcje dewelopera i w nowym menu, włączając „Android debugging” pod nagłówkiem „Debugowanie”. Po tym, wystarczy podłączyć urządzenie, zaakceptować okno dialogowe na urządzeniu potwierdzające, że jest to bezpieczne połączenie debugowania, i powinieneś być w stanie wdrożyć i uruchomić aplikację z F5. Uwaga urządzenia fizyczne mają wyższy priorytet niż emulator i będzie przełączać się do niego jako domyślnej opcji debugowania po podłączeniu.
Po wdrożeniu i załadowaniu aplikacji, zostaniesz powitany przez domyślny szablon pojedynczej strony. Otwórz menu w prawym górnym rogu, aby się zalogować, a po wprowadzeniu swoich danych należy powrócić do tej strony z wirującym paskiem postępu przed automatycznie wysyłane do strony pulpitu nawigacyjnego z wiadomością powitalną:
Learn More About Xamarin, OpenID Connect, and Secure Authentication
Jeśli po wykonaniu wszystkich tych kroków masz teraz podstawowe Android app zbudowany przy użyciu Xamarin.Android, z w pełni funkcjonalnym uwierzytelnianiem użytkownika opartym na OpenID i usłudze Okta. Stąd możesz łatwo rozszerzyć aktywność tablicy rozdzielczej, aby zaimplementować swoją funkcjonalność.
Aby zobaczyć kod z tego postu w całości, przejdź na naszą stronę GitHub.
Jeśli ten tutorial zaostrzył twój apetyt na rozwój Xamarin i chciałbyś dowiedzieć się więcej, to gorąco zachęcam do zapoznania się z innymi świetnymi artykułami:
- Add Identity Management to Your Android App
- Add Authentication to Your Xamarin.Forms App with OpenID Connect
- Build an App for iOS and Android with Xamarin