Время прочтения: 8 мин.


Разработка Android-приложений даже с несколькими пользователями (до 100 человек) подразумевает решение типовых задач, таких как работа с БД, аутентификация и безопасность, которые могут вызвать трудности у начинающих разработчиков. К счастью, в настоящее время существует несколько сервисов для автоматизации этих процессов.

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

В данном посте я рассмотрю решение типовой задачи при разработке мобильных/web-приложений, а именно добавление аутентификации пользователей с помощью облачного сервиса.  Для этого проанализирую актуальный и мощный инструмент Firebase и рассмотрю процесс подключения и реализации аутентификации по шагам.

Сравнение платформ

Сравню 4 платформы: AWS (Amazon Web Services), Firebase, Google Cloud, Microsoft Azure.

Сравнение платформ показало, что Firebase является более удобным и полностью соответствует требованиям к безопасности. Полнота сервисов не критична на этапе знакомства с платформой и может стать проблемой только в узкоспециализированных областях.

Обзор платформы Firebase

Firebase — это облачная платформа, которая предоставляет сервисы для разработки мобильных и веб-приложений, и решения их стандартных задач:

  • Authentication – аутентификация пользователей, с помощью нескольких провайдеров (соц. сети, номер телефона, почта).
  • Realtime Database и Firestore для синхронизации данных в режиме реального времени и облачного хранения данных.
  • Machine Learning для добавления продвинутых функций в приложение.
  • Сервисы уведомлений, которые могут использоваться для отправки целевых уведомлений пользователям на основе их поведения и предпочтений.
  • Инструменты анализа, которые могут помочь понять, как пользователи взаимодействуют с приложением, а также идентифицировать и исправить проблемы производительности приложения.
  • A/B-тестирование разных версий приложения и определения того, какая из них работает лучше.
  • Сервисы монетизации для интеграции рекламы в приложение и получения дохода.

Хочу отметить, что у сервиса не просто так два варианта хранения данных. В чем же их отличия?

  • Realtime Database — это база данных, основанная на JSON, которая использует древовидную структуру для хранения данных. Она поддерживает быструю синхронизацию данных в реальном времени между клиентскими приложениями и сервером. Простая модель данных -> простая в использовании.
  • irestore — это NoSQL база данных, которая использует документы для организации данных. Она предоставляет богатый функционал при создании сложных запросов для поиска данных, а также поддерживает многопоточность. Firestore имеет более гибкую модель данных, чем Realtime Database, что позволяет лучше организовывать данные и управлять ими.

Безопасность

Firebase предоставляет чек-лист безопасности. В нем объясняется, где можно хранить данные и какие инструменты нужно подключить для улучшения надежности приложения.

При использовании внешних БД удостоверьтесь, что вы соблюдаете правила безопасности:

  • Не оставляйте секретные ключи в открытом коде!
  • Проверьте, что файлы конфигурации не находятся в открытом доступе.

В рамках знакомства с платформой предлагаю подключить Firebase и Authentication. В итоге получится маленький pet-проект на Android со входом в систему.

Инструкция: подключение к Firebase

Шаг 1.

Создаю приложение с готовым пустым шаблоном (empty activity). Проверяю, что все версии зависимостей приложения соответствуют актуальным. После чего удостоверяюсь, что на эмуляторе/устройстве есть сервисы Google Play, иначе ничего не заработает. Перехожу в консоль Firebase.

Шаг 2.

Начинаю процесс подключения Firebase к разрабатываемому Android-приложению . Документация предлагает два варианта подключения, выбираю быстрый и понятный. Для этого жму иконку соответствующей платформы.

Шаг 3.

Открывается меню регистрации. Находим название пакета приложения (com.example.myappnamehere). Достаю ключ SHA-1, например, через Gradle Console в Android Studio с помощью команды gradle signingReport:

Шаг 4.

Для привязки Firebase скачиваю google-services.json. Перемещаю файл конфигурации в корневой каталог модуля (уровень <project>/build.gradle) приложения.

Для того, чтобы файл и приложение увидели друг друга, устанавливаю зависимости:

buildscript { 

    repositories {
      // Обязательно должны быть эти репозитории
      google()  //Репозиторий  Google's Maven 
      mavenCentral()  // Репозиторий  Maven Central

    dependencies {
      ...
      // Google services Gradle plugin
      classpath 'com.google.gms:google-services:4.3.15'
    }
}

// buildscript должен быть выше plugins

Переходим в <project>/<app-module>:

plugins {
    id 'com.android.application'

    // Добавим Google services Gradle plugin
    id 'com.google.gms.google-services'
    ...
}

Шаг 5. 

Добавляю нужные зависимости в файл Gradle (обычно <project>/<app-module>/build.gradle, там же где только что добавили плагины):

dependencies {
    // Импортируем Firebase BoM (https://firebase.google.com/docs/android/learn-more?hl=ru#bom)
    implementation platform('com.google.firebase:firebase-bom:31.5.0')

    // Добавим зависимости к нужным нам сервисам Firebase 
    // (в данном случае Authentication и Firestore)
    implementation 'com.google.firebase:firebase-auth'
    implementation 'com.google.firebase:firebase-firestore'
}

Шаг 6.

Нажимаю кнопку Build. Если приложение не удалось забилдить, то стоит проверить:

  • Установлены ли правильно зависимости на всех уровнях?
  • В нужном ли месте лежит файл google-services.json?
  • Что говорит терминал?

Итак, после установки основы, перейду к реализации аутентификации в проекте.

Инструкция: Настройка Firebase Authentication для авторизации пользователей

Пойду самым простым путем, воспользуюсь библиотекой FirebaseUI. Там уже все приготовлено для старта:

  • Элементы и переходы между экранами входа (e-mail, google-аккаунт, социальные сети).
  • Кастомизируемые элементы для приведения приложения в общий стиль.
  • Экраны кастомизации профиля юзера.
  • Экраны привязки аккаунтов к соц. сетям.

Шаг 1.

Подключаю нужные библиотеки. Без этого никак.

dependencies {
    implementation 'com.firebaseui:firebase-ui-auth:7.2.0'
    // Для логина через Facebook
    // Новейшие релизы Facebook SDK тут: https://goo.gl/Ce5L94
    implementation 'com.facebook.android:facebook-android-sdk:8.x'
}

Шаг 2.

Включаю опции входа в консоли. Для каждого способа придется включать отдельно.

Важно знать: для подключения входа через соц. сети, придется получить App Id и API Key. Их можно получить на странице для разработчиков нужной платформы.

Шаг 3.

Для опций входа в приложение через Twitter и/или Facebook добавляем строки в strings.xml, обратите внимание на то, что нужно указать YOUR_APP_ID.

<resources>
  <!-- Facebook application ID и custom URL scheme ('fb'+app ID). -->
  <string name="facebook_application_id" translatable="false">YOUR_APP_ID</string>
  <string name="facebook_login_protocol_scheme" translatable="false">fbYOUR_APP_ID</string>
</resources>

Как найти свой App ID?

Переходим на главную страницу консоли -> в настройки проекта -> в основных скроллим вниз -> ищем поле App ID.

После чего заново скачаю и заменю файл google-services.json.

Шаг 4.

Создаю отдельную активность LoginActivity.java для входа пользователя. Добавляем функцию ActivityResultLauncher, которая ловит ответ для FirebaseUI.

Создаю createSignInIntent, в котором указываю методы входа. Их будем создавать и запускать через signInIntent. После чего создаю функцию, в которой описываю сценарий удачного/неудачного входа в аккаунт. Если юзер зашел, то получаем объект типа FirebaseUser. При обращении к нему можно получить информацию о юзере, например, user.displayName и т.д. Если юзер не смог зайти, то ловим ошибки или возвращаемся обратно, тут уже дело за разработчиком. По умолчанию выкинет обратно на экран входа.

public class LoginActivity extends AppCompatActivity {
	FirebaseAuth mAuth;

// [START auth_fui_create_launcher]
	private final ActivityResultLauncher<Intent> signInLauncher = registerForActivityResult(
        	new FirebaseAuthUIActivityResultContract(),
        	new ActivityResultCallback<FirebaseAuthUIAuthenticationResult>() {
            	@Override
            	public void onActivityResult(FirebaseAuthUIAuthenticationResult result) {
                	onSignInResult(result);
            	}
        	}
	);
	// [END auth_fui_create_launcher]

	@Override
	protected void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.activity_login);
    	createSignInIntent();  // вызываем функцию, создающую намерение входа
	}

public void createSignInIntent() {
    	// [START auth_fui_create_intent]
    	// Перечислим способы входа
    	List<AuthUI.IdpConfig> providers = Arrays.asList(
            	new AuthUI.IdpConfig.EmailBuilder().build(),
            	new AuthUI.IdpConfig.PhoneBuilder().build(),
            	new AuthUI.IdpConfig.GoogleBuilder().build(),
            	new AuthUI.IdpConfig.TwitterBuilder().build());

    	// Создадим и запустим намерение входа
    	Intent signInIntent = AuthUI.getInstance()
            	.createSignInIntentBuilder()
            	.setAvailableProviders(providers)
            	.build();
    	signInLauncher.launch(signInIntent);
    	// [END auth_fui_create_intent]
	}
	// [START auth_fui_result]
	private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
    	IdpResponse response = result.getIdpResponse();
    	if (result.getResultCode() == RESULT_OK) {
        	// Вход успешен
        	FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

        	Intent intent = new Intent(this, MainActivity.class);
        	startActivity(intent); // Так как вход успешен, переходим на основную активность

    	} else {
        	// Обработка ошибки входа
        	assert response != null;
        	Log.e("LOGIN ERROR", response.getError().toString());
    	}
	}
	// [END auth_fui_result]

Шаг 5.

Не забываю создать файл лэйаута activity_login.xml для отображения активности.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	tools:context=".LoginActivity">

</androidx.constraintlayout.widget.ConstraintLayout>

Шаг 6.

Реализую MainActivity.java

public class MainActivity extends AppCompatActivity {
	FirebaseAuth mAuth;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.activity_main);

    	TextView txt = findViewById(R.id.txt);
    	Button btn = findViewById(R.id.btn);

    	btn.setOnClickListener(new View.OnClickListener() {
        	public void onClick(View v) {
            		signOut();
        	} //Кнопка выхода из системы
    	});

	}
	@Override
	public void onStart() {
    	super.onStart();
    	// Проверка, вошел ли пользователь и соответствующее обновление UI.

    	mAuth = FirebaseAuth.getInstance();
    	FirebaseUser currentUser = mAuth.getCurrentUser();
    	if(currentUser == null){
        	Intent intent = new Intent(this, LoginActivity.class);
        	startActivity(intent);
    	}
	}

	private void signOut() {
    	mAuth = FirebaseAuth.getInstance();
    	if (mAuth.getCurrentUser() != null) {
        		mAuth.signOut();
    	}
    	Intent intent = new Intent(this, LoginActivity.class);
    	startActivity(intent);
	}
}

И лэйаут для него:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:fitsSystemWindows="true"
	android:orientation="vertical"
	tools:context=".MainActivity" >

	<TextView
    	android:id="@+id/txt"
    	android:layout_width="match_parent"
    	android:layout_height="wrap_content"
    	android:text="You are logged in!" />

	<Button
    	android:id="@+id/btn"
    	android:layout_width="match_parent"
    	android:layout_height="wrap_content"
    	android:text="log out" />

</LinearLayout>

В итоге, я научилась подключать Firebase к приложению, реализовала вход в приложение с помощью Firebase Authentication.

Заключение

Firebase – это разносторонний инструмент для работы с данными, анализ которого и подробный разбор процесса подключения и аутентификации позволил найти простое решение типовой задачи при разработке Android-приложений, а именно добавление аутентификации пользователей с помощью облачного сервиса.

Удачи в разработке!