Аутентификация и авторизация

Аутентификация является процессом подтверждения подлинности пользователя и чаще всего реализуется через формы входа с использованием логина и пароля. В контексте Polymer аутентификация строится на компонентах веб-приложений и взаимодействии с серверными API через AJAX или Fetch.

Для начала необходимо создать пользовательский элемент формы входа:

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';

class LoginForm extends PolymerElement {
  static get properties() {
    return {
      username: { type: String, value: '' },
      password: { type: String, value: '' },
      errorMessage: { type: String, value: '' }
    };
  }

  static get template() {
    return html`
      <style>
        form { display: flex; flex-direction: column; width: 300px; }
        input { margin-bottom: 10px; padding: 8px; }
        .error { color: red; }
      </style>
      <form on-submit="handleLogin">
        <input type="text" placeholder="Имя пользователя" value="{{username::input}}">
        <input type="password" placeholder="Пароль" value="{{password::input}}">
        <button type="submit">Войти</button>
        <div class="error">[[errorMessage]]</div>
      </form>
    `;
  }

  handleLogin(event) {
    event.preventDefault();
    fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: this.username, password: this.password })
    })
    .then(response => response.json())
    .then(data => {
      if (data.success) {
        sessionStorage.setItem('authToken', data.token);
        this.errorMessage = '';
        this.dispatchEvent(new CustomEvent('login-success', { detail: data }));
      } else {
        this.errorMessage = 'Неверное имя пользователя или пароль';
      }
    })
    .catch(() => {
      this.errorMessage = 'Ошибка сервера. Попробуйте позже.';
    });
  }
}

customElements.define('login-form', LoginForm);

Ключевые моменты:

  • Двусторонняя привязка через {{}} позволяет автоматически синхронизировать данные формы с полями класса.
  • Метод fetch выполняет POST-запрос к серверу и обрабатывает ответ JSON.
  • Хранение токена аутентификации в sessionStorage обеспечивает доступ к нему в рамках текущей сессии.

Авторизация и управление доступом

После аутентификации важно ограничивать доступ к защищённым ресурсам. В Polymer это реализуется через проверку токена перед отображением определённых компонентов.

Пример компонента с авторизацией:

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';

class ProtectedPage extends PolymerElement {
  static get properties() {
    return {
      authToken: { type: String, value: '' },
      isAuthorized: { type: Boolean, value: false }
    };
  }

  connectedCallback() {
    super.connectedCallback();
    this.authToken = sessionStorage.getItem('authToken') || '';
    this.isAuthorized = !!this.authToken;
    if (!this.isAuthorized) {
      this.dispatchEvent(new CustomEvent('redirect-login'));
    }
  }

  static get template() {
    return html`
      <template is="dom-if" if="[[isAuthorized]]">
        <div>Добро пожаловать на защищённую страницу</div>
      </template>
      <template is="dom-if" if="[[!isAuthorized]]">
        <div>Доступ запрещён. Перенаправление на страницу входа...</div>
      </template>
    `;
  }
}

customElements.define('protected-page', ProtectedPage);

Ключевые моменты:

  • Свойство isAuthorized используется для условного отображения контента через <template is="dom-if">.
  • Проверка наличия токена при подключении компонента (connectedCallback) предотвращает несанкционированный доступ.
  • События типа redirect-login позволяют централизованно управлять маршрутизацией.

Роль компонентов и событий в управлении доступом

В Polymer важным инструментом для авторизации является система событий и реактивные свойства компонентов. Используя события, можно уведомлять другие части приложения о смене состояния авторизации, например, при успешном входе или выходе:

this.dispatchEvent(new CustomEvent('logout', { bubbles: true, composed: true }));
  • bubbles: true позволяет событию подниматься по дереву компонентов.
  • composed: true делает событие доступным за пределами Shadow DOM.

Это упрощает централизованное управление состоянием авторизации и обеспечивает взаимодействие между отдельными компонентами без глобальных переменных.

Интеграция с внешними API и OAuth

Polymer-компоненты легко интегрируются с внешними сервисами аутентификации через OAuth 2.0 или OpenID Connect. Основная стратегия заключается в открытии окна авторизации и обработке токена в колбеке:

handleOAuthLogin() {
  const authWindow = window.open('https://auth.example.com/oauth2/authorize', 'auth', 'width=600,height=700');
  window.addEventListener('message', (event) => {
    if (event.origin === 'https://auth.example.com') {
      sessionStorage.setItem('authToken', event.data.token);
      this.dispatchEvent(new CustomEvent('login-success', { detail: event.data }));
    }
  });
}

Особенности:

  • Использование window.postMessage обеспечивает безопасную передачу токена между окнами.
  • Токен сохраняется в sessionStorage или localStorage в зависимости от требований безопасности.
  • После получения токена выполняется dispatchEvent для уведомления компонентов приложения о смене состояния пользователя.

Управление ролями и правами доступа

Для сложных приложений важно не только проверять факт аутентификации, но и контролировать права пользователя. Пример механизма ролей:

class RoleProtectedPage extends PolymerElement {
  static get properties() {
    return {
      userRoles: { type: Array, value: () => [] },
      requiredRole: { type: String }
    };
  }

  connectedCallback() {
    super.connectedCallback();
    const token = sessionStorage.getItem('authToken');
    if (token) {
      this.userRoles = JSON.parse(atob(token.split('.')[1])).roles || [];
    }
    if (!this.userRoles.includes(this.requiredRole)) {
      this.dispatchEvent(new CustomEvent('access-denied'));
    }
  }
}
  • Декодирование JWT позволяет извлекать информацию о ролях пользователя без дополнительных запросов к серверу.
  • Проверка ролей перед отображением контента обеспечивает точный контроль доступа.

Работа с состоянием сессии и безопасностью

Для надёжной работы аутентификации и авторизации необходимо учитывать:

  • Срок действия токена: токены следует обновлять через Refresh Token или повторный вход.
  • Удаление токена при выходе: sessionStorage.removeItem('authToken').
  • Шифрование и защита данных: хранить только безопасные токены, избегать передачи паролей в открытом виде.
  • Обработка ошибок сервера и несанкционированного доступа: предусматривать уведомления и редиректы.

Эти меры позволяют создавать безопасные и масштабируемые приложения на Polymer, обеспечивая корректную аутентификацию и детализированное управление правами пользователей.