Pull to refresh

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

Reading time 6 min
Views 5.8K

Аутентификация пользователей — базовый функционал подавляющего большинства 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) для соответствующих каркасов. Обобщенных структур данных нет — каждый каркас/модуль отталкивается от "собственного представления о прекрасном". Каркасы, как правило, используют структуры с меньшим количеством атрибутов, чем модули, в силу своей большей универсальности. Зато они изначально предусматривают возможность расширения базовых структур в сторонних модулях, которые могут реализовывать альтернативные схемы аутентификации.


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

Only registered users can participate in poll. Log in, please.
При создании нового web-приложения я/мы использую(-ем) для функционала по аутентификации пользователей:
50.72% новую структуру данных, отвечающую требованиям создаваемого web-приложения 35
14.49% свою структуру данных из своего же, ранее созданного web-приложения 10
26.09% структуру данных, предоставляемую каркасом (или свое расширение этой структуры) 18
8.7% структуру данных, предоставляемую сторонним модулем (или свое расширение этой структуры) 6
0% иное 0
69 users voted. 42 users abstained.
Tags:
Hubs:
0
Comments 6
Comments Comments 6

Articles