Как стать автором
Обновить

Модели данных для аутентификации пользователей в web-приложениях

Время на прочтение6 мин
Количество просмотров5.9K

Аутентификация пользователей — базовый функционал подавляющего большинства web-приложений. Этот функционал имплементирован с помощью различных языков программирования и поддерживается различными репозиториями ("хардкод", файлы, базы данных, LDAP, ...).


image


В предыдущей своей публикации я высказал смелое заблуждение "Пока же создание очередного web-приложения зачастую начинается с проектирования собственной структуры данных для аутентификации пользователей", на что мне было скинуто несколько ссылок на некоторые имплементации аутентификации (в основном — на PHP). Под катом — сравнение структур User-моделей этих имплементаций.


Казалось бы


Аутентификация — функционал, знакомый каждому web-разработчику. Самая простая структура данных для User-модели примерно такая:


  • username
  • password

Если данные размещаются в базе данных, то зачастую дополняются еще одним (как правило, целочисленным) атрибутом:


  • id

Ну что ж, посмотрим, что предлагают web-разработчикам различные имплементации базового функционала (я не приводил различные структуры к единому виду, но суть и так понятна). DISCLAIMER: я не использовал эти модули "в бою", мои предположения основаны на рассматриваемых структурах данных — это просто мои предположения и ничего более. Если разработчики модуля в поле с именем email помещают домашний адрес пользователя, то мой выкладки однозначно введут вас в заблуждение.


Zend FW 2


ZF-Commons/ZfcUser


Самая простая схема данных из рассмотренных:


CREATE TABLE user
(
    user_id       INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
    username      VARCHAR(255) DEFAULT NULL UNIQUE,
    email         VARCHAR(255) DEFAULT NULL UNIQUE,
    display_name  VARCHAR(50) DEFAULT NULL,
    password      VARCHAR(128) NOT NULL,
    state         SMALLINT
)

Минимально необходимый набор для БД (id, username, password), плюс идентификаторы "для человеков" (email, display_name), плюс код состояния пользователя (active, inactive, ...). Уникализация значений по email'ам наводит на мысль о возможности аутентификации как по username, так и по email'у.


Laravel


php-soft/laravel-users


Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password', 60);
    $table->rememberToken();
    $table->timestamps();
});

Тоже одна из самых лаконичных схем данных. Поиск пользователя идет по "email", минимальный набор атрибутов User-модели дополнен атрибутом для имени пользователя ("name" — display name). "rememberToken()" скорее всего добавляет поддержку сохранения аутентификации для конкретного браузера ("Remember me" checkbox на аутентификационной форме). "timestamps()" предположительно добавляют даты создания и модификации отдельных записей (возможно — удаления, но маловероятно, т.к. нет атрибута состояния — state, status, etc.)


Symfony2


FriendsOfSymfony/FOSUserBundle


<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping ...>

    <mapped-superclass name="FOS\UserBundle\Model\User">
        <field name="username" column="username" type="string" length="180" />
        <field name="usernameCanonical" column="username_canonical" type="string" length="180" unique="true" />
        <field name="email" column="email" type="string" length="180" />
        <field name="emailCanonical" column="email_canonical" type="string" length="180" unique="true" />
        <field name="enabled" column="enabled" type="boolean" />
        <field name="salt" column="salt" type="string" nullable="true" />
        <field name="password" column="password" type="string" />
        <field name="lastLogin" column="last_login" type="datetime" nullable="true" />
        <field name="confirmationToken" column="confirmation_token" type="string" length="180" unique="true" nullable="true" />
        <field name="passwordRequestedAt" column="password_requested_at" type="datetime" nullable="true" />
        <field name="roles" column="roles" type="array" />
    </mapped-superclass>

</doctrine-mapping>

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


Yii 2


dektrium/yii2-user


$this->createTable('{{%user}}', [
    'id'                   => $this->primaryKey(),
    'username'             => $this->string(25)->notNull(),
    'email'                => $this->string(255)->notNull(),
    'password_hash'        => $this->string(60)->notNull(),
    'auth_key'             => $this->string(32)->notNull(),
    'confirmation_token'   => $this->string(32)->null(),
    'confirmation_sent_at' => $this->integer()->null(),
    'confirmed_at'         => $this->integer()->null(),
    'unconfirmed_email'    => $this->string(255)->null(),
    'recovery_token'       => $this->string(32)->null(),
    'recovery_sent_at'     => $this->integer()->null(),
    'blocked_at'           => $this->integer()->null(),
    'registered_from'      => $this->integer()->null(),
    'logged_in_from'       => $this->integer()->null(),
    'logged_in_at'         => $this->integer()->null(),
    'created_at'           => $this->integer()->notNull(),
    'updated_at'           => $this->integer()->notNull(),
], $this->tableOptions);

Самая сложная структура данных из расмотренных. Помимо собственной аутентификации ("auth_key" — Remember-токен?) есть подтверждение email-адреса, восстановление пароля, контроль сессии ("logged_in_from" и "logged_in_at"), время создания/изменения данных о пользователе.


Django


Базовая модель данных состоит из двух классов AbstractBaseUser и AbstractUser:


class AbstractBaseUser(models.Model):
    password = models.CharField(_('password'), max_length=128)
    last_login = models.DateTimeField(_('last login'), blank=True, null=True)

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    ...
    username = models.CharField(_('username'), max_length=150, unique=True, ...)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(_('staff status'), default=False, ...)
    is_active = models.BooleanField(_('active'), default=True, ...)
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

Тоже достаточно минимальная схема, хоть и "размазана" по двум классам. Из интересного — атрибут "is_staff", флаг допуска пользователя к админке web-приложения.


Loopback


Очень минималистичная структура данных:


{
  "name": "User",
  "properties": {
    "realm": {
      "type": "string"
    },
    "username": {
      "type": "string"
    },
    "password": {
      "type": "string",
      "required": true
    },
    "email": {
      "type": "string",
      "required": true
    },
    "emailVerified": "boolean",
    "verificationToken": "string"
  },
  ...
}

Поддерживает верификацию email'ов пользователей и вводит дополнительный атрибут realm, позволяющий разделять пользователей по "областям" (полагаю, это имеет отношение к multitenant-архитектуре, SaaS-платформам).


Spring


Структура данных также минималистична:


    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;

Расширяется набором прав пользователя и флагами состояния учетной записи.


Резюме


Готовые структуры данных для аутентификации пользователей существуют как на уровне каркасов/framework'ов (Loopback, Django, Spring), так и на уровне отдельных модулей (ZF-Commons/ZfcUser, php-soft/laravel-users, FriendsOfSymfony/FOSUserBundle, dektrium/yii2-user) для соответствующих каркасов. Обобщенных структур данных нет — каждый каркас/модуль отталкивается от "собственного представления о прекрасном". Каркасы, как правило, используют структуры с меньшим количеством атрибутов, чем модули, в силу своей большей универсальности. Зато они изначально предусматривают возможность расширения базовых структур в сторонних модулях, которые могут реализовывать альтернативные схемы аутентификации.


Ну и напоследок хотелось бы узнать, насколько сильно было мое заблуждение относительно "проектирования собственных структур данных для аутентификации пользователей".

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
При создании нового web-приложения я/мы использую(-ем) для функционала по аутентификации пользователей:
50.72% новую структуру данных, отвечающую требованиям создаваемого web-приложения35
14.49% свою структуру данных из своего же, ранее созданного web-приложения10
26.09% структуру данных, предоставляемую каркасом (или свое расширение этой структуры)18
8.7% структуру данных, предоставляемую сторонним модулем (или свое расширение этой структуры)6
0% иное0
Проголосовали 69 пользователей. Воздержались 42 пользователя.
Теги:
Хабы:
0
Комментарии6

Публикации