Android Login-Account Authenticator vs Manual Authentication

Zamierzam zaimplementować logowanie wraz z uwierzytelnianiem użytkownika w mojej aplikacji.

Moim pierwszym pomysłem było, aby zrobić to ręcznie, zarejestrować nazwę użytkownika i hasło na serwerze, uzyskać auth token, zapisać go i używać go w kolejnych żądaniach.

Po googlowaniu doszedłem do wniosku, że prawidłowym sposobem na zrobienie tego na Androidzie było użycie Account Authenticator. Widziałem kilka przykładów jego implementacji, ale nie rozumiem korzyści z robienia tego w ten sposób? Czy to dlatego, że mogę mieć więcej niż jedno konto zapisane? Czy to z powodu problemów z synchronizacją? Byłbym wdzięczny, gdyby ktoś mi to wyjaśnił. To prawdopodobnie sprawi, że lepiej zrozumiem kod do niego i dlaczego robi to, co jest.

Author: Daniel Julio, 2015-08-10

3 answers

Mogę mieć więcej niż jedno konto przechowywane?

Tak. Zobacz, Jak to robią Google lub Facebook.
Czy to z powodu problemów z synchronizacją?

Tak, potrzebujesz konta, aby użyć mechanizmu synchronizacji, takiego jak SyncAdapter

Dlaczego warto używać AccountAuthenticator?

  • Obsługa mechanizmu synchronizacji tła jak SyncAdapter;

  • Standardowy sposób uwierzytelniania użytkownicy;

  • Obsługa różnych tokenów;

  • Udostępnianie kont z różnymi uprawnieniami

Co musisz zrobić?

1). Create Authenticator;

2). Utwórz Activity dla logowania użytkownika;

3). Utwórz Service, aby komunikować się z kontem.

Warunki.

AccountManager - zarządza kontem na urządzeniu. Żądaj tokenów auth, których powinieneś używać AccountManager.

AbstractAccountAuthenticator - komponent do pracy z typami kont. Zawiera całą logikę pracy z kontem (autoryzacja, prawa dostępu itp.) Jeden AbstractAccountAuthenticator może być używany przez różne aplikacje (np. konto Google dla Gmaila, Kalendarz, dysk itp.)

AccountAuthenticatorActivity - base Activity, for authorize / create account. AccountManager wywołuje to konto, jeśli jest to konieczne do identyfikacji konta (Token nie istnieje lub expired)

Jak to wszystko działa? Spójrz na obrazek poniżej:

schemat zarządzania kontem android

Kroki.

1). Create Authenticator;

Musisz rozszerzyć AbstractAccountAuthenticator i nadpisać 7 metod:

  • Bundle editProperties(AccountAuthenticatorResponse response, String accountType) link
  • Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) link
  • Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) link
  • Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) link
  • String getAuthTokenLabel(String authTokenType) link
  • Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) link
  • Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) link

Przykład:

public class LodossAuthenticator extends AbstractAccountAuthenticator {

    private static final String LOG_TAG = LodossAuthenticator.class.getSimpleName();

    private final Context mContext;

    public LodossAuthenticator(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
        return null;
    }

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
        final Intent intent = new Intent(mContext, CustomServerAuthenticatorSigninActivity.class);
        intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType);
        intent.putExtra(Config.ARG_AUTH_TYPE, authTokenType);
        intent.putExtra(Config.ARG_IS_ADDING_NEW_ACCOUNT, true);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

        final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
    }

    @Override
    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        // If the caller requested an authToken type we don't support, then
        // return an error
        if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
            return result;
        }

        // Extract the username and password from the Account Manager, and ask
        // the server for an appropriate AuthToken.
        final AccountManager am = AccountManager.get(mContext);
        String authToken = am.peekAuthToken(account, authTokenType);

        // Lets give another try to authenticate the user
        if (TextUtils.isEmpty(authToken)) {
            final String password = am.getPassword(account);
            if (password != null) {
                try {
                    authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        // If we get an authToken - we return it
        if (!TextUtils.isEmpty(authToken)) {
            final Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
            result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
            return result;
        }

        // If we get here, then we couldn't access the user's password - so we
        // need to re-prompt them for their credentials. We do that by creating
        // an intent to display our AuthenticatorActivity.
        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
        intent.putExtra(com.lodoss.authlib.Config.ARG_ACCOUNT_TYPE, account.type);
        intent.putExtra(com.lodoss.authlib.Config.ARG_AUTH_TYPE, authTokenType);
        intent.putExtra(Config.ARG_ACCOUNT_NAME, account.name);
        final Bundle bundle = new Bundle();
    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
    return bundle;
    }

    @Override
    public String getAuthTokenLabel(String authTokenType) {
        if (AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
            return AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
        else if (AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
            return AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY_LABEL;
        else
            return authTokenType + " (Label)";
    }

    @Override
    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
        final Bundle result = new Bundle();
        result.putBoolean(KEY_BOOLEAN_RESULT, false);
        return result;
    }
}

Wyjaśnienie:

Więc musisz zobaczyć tylko 2 metody: addAccount, getAuthToken.

W addAccount dodałem kilka param config, które będą używane przez mojego Activity do logowania użytkownika. Głównym punktem jest intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType); - należy tutaj określić typ konta. Inne manipulacje nie są konieczne.

W getAuthToken - Przeczytaj komentarze proszę . Skopiowałem tę metodę z UdinicAuthenticator.java

Również, będziesz potrzebować następujących uprawnień w AndroidManifest.xml:

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

Podsumowanie z metod addAccount i getAuthToken

Spróbuj uzyskać token, jeśli token istnieje zwróć wynik, w przeciwnym razie zobaczysz Activity dla autoryzacji

2). Utwórz Activity dla logowania użytkownika;

ZobaczAuthenticator

Krótkie wyjaśnienie: Utwórz formularz z UserId i hasłem. Używanie identyfikatora użytkownika i hasła dane pobierają Token auth z serwera, a następnie wykonują następujący krok:

mAccountManager.addAccountExplicitly(account, accountPassword, null);
mAccountManager.setAuthToken(account, authtokenType, authtoken);

3). Utwórz Service, aby komunikować się z kontem.

Zobacz UdinicAuthenticatorService

Nie zapomnij dodać tej linii w AndroidManifest.xml do Service:

    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data android:name="android.accounts.AccountAuthenticator"
               android:resource="@xml/authenticator" />

A także w res/xml Dodaj plik authenticator.xml:

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                       android:accountType="com.mediamanagment.app"
                       android:icon="@drawable/ic_launcher"
                       android:smallIcon="@drawable/ic_launcher"
                       android:label="@string/authenticator_label"/>
To wszystko. Możesz użyć swojego AccountAuthenticator.

Dla materiałów źródłowych dzięki

 89
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-26 14:51:19

The AccountManager jest dobre z następujących powodów:

  • pierwszy polega na przechowywaniu wielu nazw kont z różnymi poziomami dostępu do funkcji aplikacji w ramach jednego typu konta. Na przykład w aplikacji do przesyłania strumieniowego wideo jeden może mieć dwie nazwy kont: jedno z dostępem demo do ograniczonej liczby filmów, a drugie z pełnym miesięcznym dostępem do wszystkich filmów. Nie jest to jednak główny powód używania Accounts, ponieważ możesz łatwo zarządzać tym w aplikacji bez potrzeby tego fantazyjnie wyglądająca Accounts rzecz...
  • inną zaletą korzystania z Accounts jest pozbycie się tradycyjnej autoryzacji za pomocą nazwy użytkownika i hasła za każdym razem, gdy użytkownik żąda autoryzowanej funkcji, ponieważ uwierzytelnianie odbywa się w tle, a użytkownik jest proszony o hasło tylko pod pewnymi warunkami, do czego dojdę później.
  • Korzystanie z funkcji Accounts w Androidzie również eliminuje potrzebę definiowania własnego typu konta. Prawdopodobnie natknąłeś się na aplikacje korzystające z kont Google do autoryzacji, co oszczędza kłopotów związanych z tworzeniem nowego konta i zapamiętywaniem jego danych uwierzytelniających dla użytkownika.
  • Accounts można dodawać niezależnie poprzez Ustawienia → Konta
  • wieloplatformowa autoryzacja użytkowników może być łatwo zarządzana za pomocą Accounts. Na przykład klient może uzyskać dostęp do chronionego materiału w tym samym czasie na swoim urządzeniu z Androidem i komputerze PC bez potrzeby ponownego logowania.
  • z punktu widzenia bezpieczeństwa, używając tego samego hasło w każdym żądaniu do serwera umożliwia podsłuchiwanie niezabezpieczonych połączeń. Szyfrowanie hasła nie jest tutaj wystarczające, aby zapobiec kradzieży hasła.
  • wreszcie, ważnym powodem korzystania z funkcji Accounts w Androidzie jest oddzielenie dwóch stron zaangażowanych w dowolną działalność zależną od Accounts, tak zwanego authenticatora i właściciela zasobów, bez narażania poświadczeń klienta (użytkownika). Terminy mogą wydawać się dość niejasne, ale nie poddawaj się, dopóki nie przeczytasz Następujący akapit ...

Pozwól mi rozwinąć się na tym drugim przykładzie aplikacji do przesyłania strumieniowego wideo. Firma A jest właścicielem firmy zajmującej się streamingiem wideo w ramach umowy z firmą B w celu świadczenia jej niektórym członkom usług strumieniowych premium. Firma B wykorzystuje metodę nazwy użytkownika i hasła do rozpoznawania swojego użytkownika. Aby firma A mogła rozpoznać członków premium B, jednym ze sposobów byłoby uzyskanie ich listy od B i wykorzystanie podobnego mechanizmu dopasowania nazwy użytkownika/hasła. To sposób, authenticator i właściciel zasobów są takie same (firma A). Oprócz obowiązku zapamiętywania drugiego hasła, jest bardzo prawdopodobne, że ustawią to samo hasło, co profil Swojej Firmy B do korzystania z usług od A. nie jest to oczywiście korzystne.

Aby zniwelować powyższe niedociągnięcia, wprowadzono OAuth. Jako otwarty standard autoryzacji, w powyższym przykładzie OAuth domaga się, aby autoryzacja została wykonana przez firmę B (authenticator) przez wydanie jakiegoś tokena o nazwie Access Token dla uprawnionych użytkowników (strona trzecia), a następnie dostarczenie firmie a (właściciel zasobów) tokena. Więc brak tokena oznacza brak uprawnień.

Więcej na ten temat i więcej na AccountManager Na mojej stronie internetowej pod adresem tutaj

 6
Author: Ali Nem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-12 06:23:17

W ustawieniach Androida masz konta dla swojego typu konta i stamtąd możesz dodać konto. AccountManager jest również centralnym miejscem do przechowywania poświadczeń, więc logujesz się tylko raz dla każdego vendera. Jeśli pobierasz inną aplikację google lub korzystasz z niej wielokrotnie, dane uwierzytelniające wpisujesz tylko raz

 0
Author: Pomagranite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-24 02:51:01