357  /  381
Справочник

Внешняя авторизация

Просмотров: 40166
Дата последнего изменения: 27.10.2020
Роберт Басыров
Сложность урока:
2 уровень - несложные понятия и действия, но не расслабляйтесь.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет

Иногда для авторизации (проверки имени входа и пароля) пользователя бывает необходимо использовать свои алгоритмы проверки и(или) внешние БД для хранения пользователей. Например, уже имеется база пользователей и необходимо дать им возможность авторизовываться на сайте под управлением CMS. В таких случаях, иногда, можно просто перенести всех пользователей в БД CMS, используя API функции, но зачастую это просто невозможно по двум причинам:

  • Первая - пароли пользователей во внешней БД не хранятся в открытом виде, хранится только хэш от них, поэтому после переноса пользователи не смогут авторизоваться по причине несовпадения пароля.
  • Вторая причина заключается в том, что иногда необходимо чтобы база пользователей была единой, т.е. пароли пользователей хранились на одном удаленном сервере и использовались для проверки в нескольких местах, в том числе и в CMS.

Для решения таких задач в Bitrix Framework, реализована возможность добавить к стандартной встроенной системе авторизации свою внешнюю. Для этого необходимо реализовать несколько шагов, которые разберем на примере внешней авторизации, используя БД пользователей популярного форума PHP BB.

Для начала создадим файл, например, /bitrix/php_interface/scripts/phpbb.php. В этом файле будет размещён класс с внешним обработчиком, назовем его __PHPBB2Auth:

class __PHPBB2Auth
{
}

Чтобы во время попытки авторизоваться вызвалась наша функция, необходимо установить обработчик события OnUserLoginExternal, который будет вызываться автоматически каждый раз при вводе пользователем имени входа и пароля, перед встроенной проверкой. Для этого в файле /bitrix/php_interface/init.php воспользуемся функцией AddEventHandler:

AddEventHandler(
     "main", 
     "OnUserLoginExternal", 
     Array("__PHPBB2Auth", "OnUserLoginExternal"), 
     100, 
     $_SERVER['DOCUMENT_ROOT'].'/bitrix/php_interface/scripts/phpbb.php'
);

В качестве обработчика мы указали метод нашего класса __PHPBB2Auth::OnUserLoginExternal. Обработчик события OnUserLoginExternal принимает в качестве параметра ссылку на массив с полями для проверки:

define("PHPBB2_TABLE_PREFIX", "phpbb_");
function OnUserLoginExternal(&$arArgs)
{
    $table_user = PHPBB2_TABLE_PREFIX."users";
    $table_user_group = PHPBB2_TABLE_PREFIX."user_group";
    extract($arArgs);

    global $DB, $USER, $APPLICATION;

    $strSql = "SELECT * FROM ".
              $table_user.
              " WHERE username='".
              $DB->ForSQL($login).
              "' AND user_password='".
              $DB->ForSql(md5($password))."'";
    $dbRes = $DB->Query($strSql);
    if($arRes = $dbRes->Fetch())
    {
        if($arRes['user_active']!='0')
        {
            // имя пользователя и пароль подошел
        }
    }
}

После того как имя входа и пароль проверены по алгоритму PHPBB, необходимо создать внешнего пользователя во внутренней БД, чтобы к нему можно было привязывать внутренние объекты (новости, голосования и т.п.). Для этого воспользуемся методом CUser::GetList с фильтром по имени входа и коду внешнего источника. Если такой пользователь не существует - создадим его, если существует - обновим информацию о нем.

$arFields = Array(
    "LOGIN" => $login,
    "NAME" => $login,
    "PASSWORD" => $password,
    "EMAIL" => $arRes['user_email'],
    "ACTIVE" => "Y",
    "EXTERNAL_AUTH_ID"=>"PHPBB2",
    "LID" => SITE_ID
    );
$oUser = new CUser;
$res = CUser::GetList($O, $B, Array("LOGIN_EQUAL_EXACT"=>$login, "EXTERNAL_AUTH_ID"=>"PHPBB2"));
if(!($ar_res = $res->Fetch()))
    $ID = $oUser->Add($arFields);
else
{
    $ID = $ar_res["ID"];
    $oUser->Update($ID, $arFields);
}
if($ID>0)
{
   // можно авторизовывать
   return $ID;
}

Теперь у нас есть идентификатор пользователя в нашей БД и можно его вернуть из функции обработчика, чтобы этот пользователь был авторизован системой, но новый пользователь будет анонимным, т.к. не привязан ни к одной из групп. Воспользуемся привязкой в базе PHPBB, чтобы перенести ее в нашу БД до авторизации.

$USER->SetParam("PHPBB2_USER_ID", $arRes['user_id']);
$groups_map = Array(
    /*'PhpBB2 Group ID' => 'Local Group ID',*/
     '2' => '1'
    );

$user_groups = Array();
$dbUserGroup = $DB->Query('SELECT * FROM '.$table_user_group.' WHERE user_id='.$arRes['user_id']);
while($arUserGroup = $dbUserGroup->Fetch())
    $user_groups[] = $arUserGroup['group_id'];

if(count($user_groups)>0)
{
    $arUserGroups = CUser::GetUserGroup($ID);
    foreach($groups_map as $ext_group_id => $group_id)
    {
        if(in_array($ext_group_id, $user_groups))
            $arUserGroups[] = $group_id;
        else
        {
            $arUserGroupsTmp = Array();
            foreach($arUserGroups as $grid)
                if($grid != $group_id)
                    $arUserGroupsTmp[] = $grid;
            $arUserGroups = $arUserGroupsTmp;
        }
    }
    CUser::SetUserGroup($ID, $arUserGroups);
}

Все, теперь локальный аккаунт пользователя соответствует удаленному, можно возвращать код пользователя и он будет авторизован. Отключим предварительно функцию запомнить меня на этом компьютере, если пользователь нажал на "флажок", т.к. при этом мы не сможем корректно проверить права доступа:

$arArgs["store_password"] = "N";
return $ID;

Для того чтобы зарегистрировать новую внешнюю проверку авторизации в системе, необходимо обработать событие OnExternalAuthList. Добавим в файл /bitrix/php_interface/init.php соответствующий вызов:

AddEventHandler(
     "main", 
     "OnExternalAuthList", 
     Array("__PHPBB2Auth", "OnExternalAuthList"), 
     100, 
     $_SERVER['DOCUMENT_ROOT'].'/bitrix/php_interface/scripts/phpbb.php'
     );

Функция обработчик должна вернуть массив из набора обработчиков с полямим ID и NAME.

function OnExternalAuthList()
{
    return Array(
        Array("ID"=>"PHPBB2", "NAME"=>"PhpBB2")
        );
}

Теперь на странице редактирования пользователя появится выпадающий список со списком внешних источников авторизации. Приведем полный текст файла /bitrix/php_interface/scripts/phpbb.php. В нем дополнительно реализован обратный механизм: при авторизации пользователя в нашей системе он автоматически авторизуется на форуме.

<?
define("PHPBB2_TABLE_PREFIX", "phpbb_");

class __PHPBB2Auth
{
    function OnUserLoginExternal(&$arArgs)
    {
        ////////// <settings> ////////////
        $table_user = PHPBB2_TABLE_PREFIX."users";
        $table_user_group = PHPBB2_TABLE_PREFIX."user_group";
        $groups_map = Array(
            /*'PhpBB2 Group ID' => 'Local Group ID',*/
            '2' => '1'
            );
        ////////// </settings> ////////////
        extract($arArgs);

        global $DB, $USER, $APPLICATION;

        $strSql = "SELECT * FROM ".
                  $table_user.
                  " WHERE username='".
                  $DB->ForSQL($login).
                  "' AND user_password='".
                  $DB->ForSql(md5($password))."'";
        $dbRes = $DB->Query($strSql);
        if($arRes = $dbRes->Fetch())
        {
            if($arRes['user_active']!='0')
            {
                $arFields = Array(
                    "LOGIN" => $login,
                    "NAME" => $login,
                    "PASSWORD" => $password,
                    "EMAIL" => $arRes['user_email'],
                    "ACTIVE" => "Y",
                    "EXTERNAL_AUTH_ID"=>"PHPBB2",
                    "LID" => SITE_ID
                    );
                $oUser = new CUser;
                $res = CUser::GetList($O, $B, 
                                      Array("LOGIN_EQUAL_EXACT"=>$login, 
                                            "EXTERNAL_AUTH_ID"=>"PHPBB2"));
                if(!($ar_res = $res->Fetch()))
                    $ID = $oUser->Add($arFields);
                else
                {
                    $ID = $ar_res["ID"];
                    $oUser->Update($ID, $arFields);
                }

                if($ID>0)
                {
                    $USER->SetParam("PHPBB2_USER_ID", $arRes['user_id']);

                    $user_groups = Array();
                    $dbUserGroup = $DB->Query('SELECT * FROM '.
                                              $table_user_group.
                                              ' WHERE user_id='.$arRes['user_id']);
                    while($arUserGroup = $dbUserGroup->Fetch())
                        $user_groups[] = $arUserGroup['group_id'];

                    if(count($user_groups)>0)
                    {
                        $arUserGroups = CUser::GetUserGroup($ID);
                        foreach($groups_map as $ext_group_id => $group_id)
                        {
                            if(in_array($ext_group_id, $user_groups))
                                $arUserGroups[] = $group_id;
                            else
                            {
                                $arUserGroupsTmp = Array();
                                foreach($arUserGroups as $grid)
                                    if($grid != $group_id)
                                        $arUserGroupsTmp[] = $grid;
                                $arUserGroups = $arUserGroupsTmp;
                            }
                        }
                        CUser::SetUserGroup($ID, $arUserGroups);
                    }
                    $arArgs["store_password"] = "N";

                    return $ID;
                }
            }
        }
    }

    function OnExternalAuthList()
    {
        return Array(
            Array("ID"=>"PHPBB2", "NAME"=>"PhpBB2 Board")
            );
    }

    function OnAuthorize(&$arArgs)
    {
        extract($arArgs);

        global $DB, $APPLICATION, $USER;
        $user_id = $USER->GetParam("PHPBB2_USER_ID");
        if($user_id<=0)
            return;
        $table_user = PHPBB2_TABLE_PREFIX."users";
        $table_sessions = PHPBB2_TABLE_PREFIX."sessions";
        $table_config = PHPBB2_TABLE_PREFIX."config";

        $dbConfig = $DB->Query("SELECT * FROM ".
                               $table_config.
                               " WHERE config_name
                                 IN ('cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure')");
        while($arConfig = $dbConfig->Fetch())
            ${$arConfig['config_name']} = $arConfig['config_value'];

        if (isset($HTTP_COOKIE_VARS[$cookie_name . '_sid']) || 
            isset($HTTP_COOKIE_VARS[$cookie_name . '_data']))
            $session_id = isset($HTTP_COOKIE_VARS[$cookie_name . '_sid']) ? 
                               $HTTP_COOKIE_VARS[$cookie_name . '_sid'] : '';

        $ip_sep = explode('.', $_SERVER['REMOTE_ADDR']);
        $user_ip = sprintf('%02x%02x%02x%02x', $ip_sep[0], $ip_sep[1], $ip_sep[2], $ip_sep[3]);
        $current_time = time();
        $sql =
            "UPDATE ".$table_sessions." SET ".
            "    session_user_id = ".$user_id.", ".
            "    session_start = ".$current_time.", ".
            "    session_time = ".$current_time.", ".
            "    session_page = 0, ".
            "    session_logged_in = 1 ".
            "WHERE session_id = '".$DB->ForSQL($session_id)."' ".
            "    AND session_ip = '".$user_ip."'";

        $r = $DB->Query($sql);
        if($r->AffectedRowsCount()<=0)
        {
            $session_id = md5(uniqid($user_ip));
            $sql =
                "INSERT INTO ".
                $table_sessions.
                "(session_id, session_user_id, session_start, session_time, session_ip, session_page, session_logged_in)".
                "VALUES ('".$session_id."', ".$user_id.", ".$current_time.", ".$current_time.", '".$user_ip."', 0, 1)";
            $DB->Query($sql);
        }

        $sql =
            "UPDATE ".$table_user." SET ".
            "    user_session_time = ".$current_time.", ".
            "    user_session_page = 0, ".
            "    user_lastvisit = ".$current_time." ".
            "WHERE user_id = ".$user_id;

        $DB->Query($sql);

        $sessiondata = Array('userid' => $user_id);

        setcookie($cookie_name.'_data', 
                  serialize($sessiondata), 
                  $current_time + 31536000, 
                  $cookie_path, 
                  $cookie_domain, $cookie_secure);
        setcookie($cookie_name.'_sid',
                  $session_id, 0, $cookie_path, 
                  $cookie_domain, $cookie_secure);
    }
}
?>

Следующие строки необходимо добавить в /bitrix/php_interface/init.php:

<?
AddEventHandler(
    "main", 
    "OnUserLoginExternal", 
    Array("__PHPBB2Auth", "OnUserLoginExternal"),
    100, 
    $_SERVER['DOCUMENT_ROOT'].'/bitrix/php_interface/scripts/phpbb.php'
    );

AddEventHandler(
    "main", 
    "OnExternalAuthList", 
    Array("__PHPBB2Auth", "OnExternalAuthList"),
    100,
    $_SERVER['DOCUMENT_ROOT'].'/bitrix/php_interface/scripts/phpbb.php'
    );

AddEventHandler(
    "main", 
    "OnAfterUserAuthorize", 
    Array("__PHPBB2Auth", "OnAuthorize"),
    100
    );
?>

В качестве примера ещё один скрипт внешней авторизации - для форума Invision Power Board:

<?
define("IPB_TABLE_PREFIX", "ibf_");
define("IPB_VERSION", "2");

AddEventHandler("main", "OnUserLoginExternal", Array("__IPBAuth", "OnUserLoginExternal"));
AddEventHandler("main", "OnExternalAuthList", Array("__IPBAuth", "OnExternalAuthList"));

class __IPBAuth
{
    function OnUserLoginExternal(&$arArgs)
    {
        extract($arArgs);

        ////////// <settings> ////////////
        $table_user = IPB_TABLE_PREFIX."members";
        $table_converge = IPB_TABLE_PREFIX."members_converge";
        $groups_map = Array(
            /*'IPB Group ID' => 'Local Group ID',*/
            '4' => '1'
            );
        ////////// </settings> ////////////

        global $DB, $USER, $APPLICATION;

        if(IPB_VERSION == '1')
        {
            $strSql = "SELECT * FROM ".$table_user." WHERE name='".$DB->ForSql($login)."' AND password='".md5($password)."'";
        }
        else
        {
            $strSql =
                "SELECT t1.* ".
                "FROM ".$table_user." t1, ".$table_converge." t2 ".
                "WHERE t1.name='".$DB->ForSql($login)."' ".
                "    AND t1.email = t2.converge_email ".
                "    AND t2.converge_pass_hash = MD5(CONCAT(MD5(t2.converge_pass_salt), '".md5($password)."'))";
        }

        $dbAuthRes = $DB->Query($strSql);
        if($arAuthRes = $dbAuthRes->Fetch())
        {
            $arFields = Array(
                "LOGIN" => $login,
                "NAME" => $arAuthRes['title'],
                "PASSWORD" => $password,
                "EMAIL" => $arAuthRes['email'],
                "ACTIVE" => "Y",
                "EXTERNAL_AUTH_ID"=>"IPB",
                "LID" => SITE_ID
                );

            $oUser = new CUser;
            $res = CUser::GetList($O, $B, Array("LOGIN_EQUAL_EXACT"=>$login, "EXTERNAL_AUTH_ID"=>"IPB"));
            if(!($ar_res = $res->Fetch()))
                $ID = $oUser->Add($arFields);
            else
            {
                $ID = $ar_res["ID"];
                $oUser->Update($ID, $arFields);
            }

            if($ID>0)
            {
                $USER->SetParam("IPB_USER_ID", $arAuthRes['id']);

                $user_group = $arAuthRes['mgroup'];
                $arUserGroups = CUser::GetUserGroup($ID);
                foreach($groups_map as $ext_group_id => $group_id)
                {
                    if($ext_group_id==$user_group)
                        $arUserGroups[] = $group_id;
                    else
                    {
                        $arUserGroupsTmp = Array();
                        foreach($arUserGroups as $grid)
                            if($grid != $group_id)
                                $arUserGroupsTmp[] = $grid;
                        $arUserGroups = $arUserGroupsTmp;
                    }
                }
                CUser::SetUserGroup($ID, $arUserGroups);
                $arArgs["store_password"] = "N";

                return $ID;
            }
        }
    }

    function OnExternalAuthList()
    {
        return Array(
            Array("ID"=>"IPB", "NAME"=>"Invision Power Board")
            );
    }
}
?>

Для того чтобы данный скрипт начал работать, его необходимо подключить в /bitrix/php_interface/init.php.

Курсы разработаны в компании «1С-Битрикс»

Если вы нашли неточность в тексте, непонятное объяснение, пожалуйста, сообщите нам об этом в комментариях.
Развернуть комментарии