Подтвердить что ты не робот

Android Login - Аутентификация учетной записи и проверка подлинности вручную

Я собираюсь внедрить логин вместе с аутентификацией пользователя в своем приложении.

Моя первая идея состояла в том, чтобы сделать это вручную, зарегистрировать имя пользователя и пароль с сервером, получить токен аутентификации, сохранить его и использовать в последующих запросах.

После поиска в Google, я понял, что правильным способом сделать это на Android было использование Account Authenticator. Я видел несколько примеров его реализации, но я не понимаю преимущества этого в этом отношении? Это потому, что я могу хранить несколько учетных записей? Это из-за проблем синхронизации? Я был бы признателен, если бы кто-нибудь мог мне это объяснить. Вероятно, это заставило бы меня лучше понять код для него и почему он делает то, что он есть.

4b9b3361

Ответ 1

У меня может быть более одной учетной записи?

Да. Посмотрите, как это сделать Google или Facebook.

Это из-за проблем с синхронизацией?

Да, вам нужна учетная запись, чтобы использовать механизм синхронизации, например SyncAdapter

Почему вы должны использовать AccountAuthenticator?

  • Поддержка механизма синхронизации фона, например SyncAdapter;

  • Стандартный способ аутентификации пользователей;

  • Поддержка разных токенов;

  • Совместное использование учетных записей с различными привилегиями

Что вам нужно сделать?

1). Создать Authenticator;

2). Создайте Activity для входа пользователя;

3). Создайте Service для связи с учетной записью.

Условия.

AccountManager - он управляет учетной записью на устройстве. Запросите токены, которые вы должны использовать AccountManager.

AbstractAccountAuthenticator - компонент для работы с типами учетных записей. Он содержит всю логику работы с учетной записью (авторизация, права доступа и т.д.). Один AbstractAccountAuthenticator может использоваться другим приложением (например, для учетной записи Google для Gmail, календаря, диска и т.д.).

AccountAuthenticatorActivity - база Activity, для авторизации/создания учетной записи. AccountManager вызывает эту учетную запись, если необходимо идентифицировать учетную запись (токен не существует или истек)

Как все это работает? Посмотрите на изображение ниже:

диаграмма управления аккаунтом Android

Steps.

1). Создать Authenticator;

Вам нужно расширить AbstractAccountAuthenticator и переопределить 7 методов:

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

Пример:

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

Пояснение:

Итак, вам нужно увидеть только 2 метода: addAccount, getAuthToken.

В addAccount Я добавил некоторые параметры конфигурации, которые будут использоваться моим Activity для входа пользователя. Главное здесь intent.putExtra(Config.ARG_ACCOUNT_TYPE, accountType); - здесь вы должны указать тип учетной записи. Другие манипуляции не нужны.

В getAuthToken - Прочитайте комментарии. Я скопировал этот метод с UdinicAuthenticator.java

Кроме того, вам понадобятся следующие разрешения в вашем 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" />

Резюме из методов addAccount и getAuthToken

Попробуйте получить токен, если токен имеет результат возврата, иначе вы увидите Activity для авторизации

2). Создайте Activity для входа пользователя;

См. AuthenticatorActivity

Краткое объяснение: Создайте форму с UserId и Password. Используя данные UserId и Password, получите аутентификационный токен с сервера, а затем выполните следующий шаг:

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

3). Создайте Service для связи с учетной записью.

См. UdinicAuthenticatorService

Не забудьте добавить эту строку в AndroidManifest.xml в Service:

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

А также в res/xml добавить файл 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"/>

Это все. Вы можете использовать AccountAuthenticator.

Для исходных материалов благодаря

Ответ 2

AccountManager хороша по следующим причинам:

  • Сначала нужно сохранить несколько имен учетных записей с различными уровнями доступа к функциям приложений под одним типом учетной записи. Например, в приложении для потоковой передачи видео могут быть два имени учетной записи: один с демо-доступом к ограниченному количеству видеороликов, а другой - полный месяц доступа ко всем видео. Однако это не основная причина использования Accounts, поскольку вы можете легко управлять этим в своем приложении без необходимости в этом причудливом стиле Accounts....
  • Другим преимуществом использования Accounts является избавление от традиционной авторизации с именем пользователя и паролем каждый раз, когда авторизованная функция запрашивается пользователем, поскольку проверка подлинности происходит в фоновом режиме, и пользователю предлагается ввести пароль только в определенном состоянии, которое я получу позже.
  • Использование функции Accounts в android также устраняет необходимость в определении собственного типа учетной записи. Вероятно, вы сталкивались с приложениями, использующими учетные записи Google, для авторизации, что избавляет от необходимости создавать новую учетную запись и запоминать ее учетные данные для пользователя.
  • Accounts можно добавить независимо через Настройки → Учетные записи
  • Авторизовать пользователя через кросс-платформу можно легко с помощью Accounts. Например, клиент может одновременно получить доступ к защищенному материалу в своем Android-устройстве и ПК без необходимости повторного входа в систему.
  • С точки зрения безопасности использование одного и того же пароля в каждом запросе на сервер позволяет осуществлять подслушивание в незащищенных соединениях. Шифрование пароля здесь недостаточно, чтобы предотвратить кражу паролей.
  • Наконец, важной причиной использования функции Accounts в android является разделение двух сторон, вовлеченных в любой бизнес, зависящий от Accounts, так называемого аутентификатора и владельца ресурса, без ущерба для учетных данных клиента (пользователя). Термины могут показаться довольно расплывчатыми, но не сдавайтесь, пока вы не прочитаете следующий параграф... 😉

Позвольте мне подробно остановиться на последнем примере приложения для потоковой передачи видео. Компания A является владельцем видеопотока в контракте с Компанией B, чтобы предоставить своим определенным участникам услуги премиум-потоковой передачи. Компания B использует метод имени пользователя и пароля для распознавания своего пользователя. Чтобы компания A узнала премиум-членов B, одним из способов было бы получить их список из B и использовать аналогичный механизм совпадения имени пользователя и пароля. Таким образом, аутентификатор и владелец ресурсов являются одинаковыми (компания A). Помимо обязательности пользователей помнить второй пароль, очень вероятно, что они устанавливают тот же пароль, что и профиль своей компании B для использования сервисов от A. Это, очевидно, не выгодно.

Чтобы устранить указанные недостатки, был введен OAuth. В качестве открытого стандарта для авторизации в приведенном выше примере OAuth требует, чтобы авторизация выполнялась компанией B (аутентификатором) путем выдачи некоторого токена под названием Access Token для подходящих пользователей (стороннего), а затем предоставления Компании A (владельцу ресурса) токен. Таким образом, никакой токен не означает права на участие.

Я подробно рассказал об этом и более на AccountManager на своем веб-сайте здесь

Ответ 3

В настройках Android у вас есть учетные записи для вашего типа учетной записи, и оттуда вы можете добавить учетную запись. AccountManager также является центральным местом для хранения учетных данных, поэтому вы регистрируете только один раз для каждого участника. Если вы загружаете другое приложение Google или получаете доступ к приложению несколько раз, вы вводите только учетные данные один раз