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.
3 answers
Tak. Zobacz, Jak to robią Google lub Facebook.Mogę mieć więcej niż jedno konto przechowywane?
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
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)
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
Daniel Serdyukov (cały tekst przetłumaczony z jego artykułu (oprócz mojego małe dodatki) " synchronizacja w aplikacjach na Androida. Część 1 " tylko po rosyjsku. link: http://habrahabr.ru/company/e-Legion/blog/206210/)
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ącaAccounts
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ą odAccounts
, 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
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
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