Вы здесь

Интеграция пользователей с внешней CRM: не логинить пользователя на сайте автоматом (по кукам) при определенном условии

0

Доброго времени суток, уважаемые гуру! Ситуация такая: есть некая самописная CRM система, работающая на том же домене, что и сайт на D8. Нужно, чтобы пользователи логинились на сайте с логином/паролем от той самой CRM. Я реализовал это следующим образом:
Кастомным модулем рисую форму для ввода логина/пароля, которую засылаю на специальный скрипт CRM (обычный атрибут action у формы). При успешной авторизации этот скрипт ставит некоторые куки и редиректит на другую страницу все того же кастомного модуля. На этой странице проверяются куки и снова делается запрос к CRM, но уже с другими параметрами (id пользователя из куков). В ответ получаю XML с информацией о пользователе. Проверяю, есть ли на сайте пользователь с таким e-mail, если нет, то создаю его и авторизую. Вот примерный код такой авторизации:

<?php
public function profile()
    {
        $s_id = $_COOKIE['s_id'];
        $h_id = $_COOKIE['h_id'];
        $context  = stream_context_create(array('http' => array('header' => 'Accept: application/xml')));
        $url = 'http://crm.server.ru/getuser&s_id='.$s_id."&h_id=".$h_id;
        $string = file_get_contents($url, false, $context);
        $xml = file_get_contents($url, false, $context);
        $user_info = new \SimpleXMLElement($xml);
        $user = \Drupal\user\Entity\User::load(58);
        $query = \Drupal::entityQuery('user')
            ->condition('field_email', $user_info->email);
        $users = $query->execute();
        //Если не нашли пользователя, то создаем его
        if(empty($users))
        {
            $user = User::create();
            $user->setUsername($user_info->firstname." ".$user_info->lastname);
            $user->setEmail($user_info->email);
            $user->activate();
            $result = $user->save();
        }
        $user = User::load(array_keys($users)[0]); //Загружаем пользователя
        user_login_finalize($user); //Авторизуем пользователя на сайте
        //Рисуем страницу приветсвия
        $output['#title'] = $user_info->firstname;
        $output['#markup'] = "Добро пожаловать, ".$user_info->firstName."!";
        return $output;
    }
?>

Все работает прекрасно - пользователи авторизуются, другим модулем с помощью node_access() из CRM им назначаются права на конкретные ноды... Проблема возникла в следующем: Drupal по умолчанию сохраняет авторизацию пользователя после закрытия окна. То есть, когда пользователь через некоторое время повторно заходит на сайт, то Drupal сразу его авторизует, не запрашивая CRM систему. В итоге получается, что на сайте пользователь авторизован, но куков CRM системы нет. И когда пользователь пытается перейти из личного кабинета Drupal'а в CRM систему, последняя посылает его по батюшке.
Связался с разработчиками CRM - есть специальный адрес для проверки авторизации, который возвращает ~~~xml
Необходима авторизация,
~~~
если пользователь не авторизован. Сессия в CRM ставится на 3 часа и продляется после каждого обращения. Отсюда два вопроса:
1)как можно при любом обращении к D8 запрашивать данный адрес и при получении ошибки НЕ логинить (ну, или разлогинить) пользователя на сайте, даже если у него есть куки самого друпала?
2)как при серфинге посетителем сайта отправлять запросы и в CRM для поддержания актуального времени сессии?
Насколько понял, для добавления своей логики при запросе D8 можно использовать EventSubscriber, но чего-то все никак не разберусь, как с ним работать :( Да и будет ли в нем доступен объект текущего пользователя или он выполняется до его инициализации?
P.S.: У нас свой небольшой дата-центр, и все внутренние ресурсы расположены физически на одной "железке", так что обмен данными между CRM и сайтом происходит практически мгновенно.

Версия Drupal: 
8.x
Вопрос задан 22.06.2016 - 00:13

Ответы

0

Как и предполагал, вопрос решился с помощью EventSubscriber. Более внимательно изучил его доки и вот что намудрил:

<?php

namespace Drupal\checkauth\EventSubscriber;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CheckAuthSubscriber extends ControllerBase implements EventSubscriberInterface {


  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['CheckAuth', 100];
    return $events;
  }

  public function CheckAuth(GetResponseEvent $event)
  {
    //Тут проверяем наличие куков от внешней системы и делаем запрос на проверку сессии. Если сессия просрочена, вызываем user_logout()
  }
}

~~~
Насколько понял из доков, KernelEvents::REQUEST будет вызываться при любом запросе к сайту.

Ответ дан 24.06.2016 - 14:24