Заставить Laravel войти в систему пользователя, используя устаревшую аутентификацию

Я пытаюсь медленно интегрировать Laravel в устаревшее PHP-приложение. Одна из задач — автоматически зарегистрировать сеанс пользователя Laravel, когда пользователь входит в старое приложение. Я не пытаюсь реализовать аутентификацию Laravel, я просто хочу воспользоваться существующими функциями и заставить конкретного пользователя войти в систему без проверки учетных данных. То, что у меня есть до сих пор, было выковано из чужих хаков, которые я нашел вокруг:

// Laravel authentication hook - Boostrap application
require_once getcwd() . '/../laravel/bootstrap/autoload.php';
$app = require_once getcwd() . '/../laravel/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$kernel->bootstrap();
$app->boot();

// Start Laravel session
$request = Illuminate\Http\Request::capture();
$response = $app->make('Symfony\Component\HttpFoundation\Response');
$startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
// Associate server session with the authenticating user id
// I have also tried loading user model instance and then $app['auth']->login($user)
$app['auth']->loginUsingId($user_id);

$app['session']->driver()->start();
// Terminate middleware response chain with naked response
$response = $startSession->handle($request, function() use($response) {
return $response; // This response will have session cookie attached to it
});

$response->send();

После этого я получаю laravel_session файл cookie, содержащий содержимое на клиенте. Во время запроса на вход после выполнения кода выше, если я dd(Auth::user()) тогда я получаю пользователя, с которым я только что вошел. Однако по последующим запросам Auth::user() а также $this->request->user() оба возвращаются null во всех контекстах.

Как я могу принудительно активировать сеанс пользователя Laravel без аутентификации, которая будет сохраняться при разных запросах?


Конечным результатом является то, что Laravel будет работать как «субприложение» под унаследованным приложением, в то время как существующие функции будут вытягиваться один за другим, так что оба будут существовать в течение некоторого времени, пока все функции не будут реализованы в Laravel, и это заменит существующее приложение в полном объеме. Если имеет больше смысла пытаться перенять устаревшую аутентификацию с помощью Laravel, а не наоборот, я открыт для этого, но я бы предпочел не менять базовую таблицу пользователей (устаревшая аутентификация происходит через LDAP, поэтому локально паролей нет, токена запомнить_, но добавить его достаточно легко, если я иметь к). Я действительно просто ищу кратчайший путь с наименьшим количеством усилий / головной боли.

8

Решение

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

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

Считайте, что это называется файл login.php, который требует $user_id войти в систему пользователя по идентификатору в laravel.

<?php

require_once getcwd() . '/../laravel/bootstrap/autoload.php';
$app = require_once getcwd() . '/../laravel/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$kernel->bootstrap();
$app->boot();

$request = Illuminate\Http\Request::capture();
$response = $app->make('Symfony\Component\HttpFoundation\Response');
$startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
$encryptCookies = new App\Http\Middleware\EncryptCookies($app['encrypter']);

$app['session']->driver()->start();

$app['auth']->loginUsingId($user_id);

$response = $encryptCookies->handle($request, function ($request) use ($startSession, $response)
{
return $startSession->handle($request, function () use ($response)
{
return $response;
});
});

$app['session']->driver()->save();

var_dump($app['auth']->user());

$response->send();

Внутри закрытия $encryptCookies->handle()Вы можете прочитать запрос после расшифровки, и здесь вы можете изменить сеанс. А когда вы вернете ответ, он снова будет зашифрован, и вы сможете отправить его в браузер.

Чтобы прочитать сеанс в другом файле, вы можете просто сделать это:

<?php

require_once getcwd() . '/../laravel/bootstrap/autoload.php';
$app = require_once getcwd() . '/../laravel/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$kernel->bootstrap();
$app->boot();

$request = Illuminate\Http\Request::capture();
$response = $app->make('Symfony\Component\HttpFoundation\Response');
$startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
$encryptCookies = new App\Http\Middleware\EncryptCookies($app['encrypter']);

$response = $encryptCookies->handle($request, function ($request) use ($startSession, $response)
{
return $startSession->handle($request, function () use ($response)
{
return $response;
});
});

var_dump($app['auth']->user());

$response->send();
2

Другие решения

Ты не звонишь save() на сессию, поэтому ваша сессия не сохраняется. Я изменил последние несколько строк вашего кода следующим образом:

// Laravel authentication hook - Boostrap application
require_once getcwd() . '/../laravel/bootstrap/autoload.php';
$app = require_once getcwd() . '/../laravel/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$kernel->bootstrap();
$app->boot();

// Start Laravel session
$request = Illuminate\Http\Request::capture();
$response = $app->make('Symfony\Component\HttpFoundation\Response');
$startSession = new Illuminate\Session\Middleware\StartSession($app['session']);
// Associate server session with the authenticating user id
// I have also tried loading user model instance and then $app['auth']->login($user)
$app['auth']->loginUsingId($user_id);

$app['session']->driver()->start();
// Terminate middleware response chain with naked response
$response = $startSession->handle($request, function() use($response) {
return $response; // This response will have session cookie attached to it
});

// Added the following two lines
var_dump($app['auth']->user()); //This is for debugging
$app['session']->driver()->save();

$response->send();

Я скопировал этот код, только удалив $app['auth']->loginUsingId($user_id); во второй файл и запустил его, и дамп показывает, что тот же пользователь все еще вошел в систему. Выход из системы и удаление строки, которая сохраняет сеанс, остановит эту работу. Надеюсь, это решение, которое вы искали.

1

Я бы посоветовал рассмотреть возможность использования Laravel в качестве основного приложения, позволяющего перейти к устаревшему коду для маршрутов, которые вы еще не конвертировали. Я сделал это с огромным унаследованным приложением, в котором было все, что вы ожидаете от приложения, написанного 10 лет назад, и хотя заставить все это работать вместе было очень трудно в начале (похоже на проблемы, которые у вас возникают, но все наоборот), когда он закончился, я просто удалял старый код, пока ничего не осталось. С вашим подходом я не вижу, что произойдет, когда придет время окончательно удалить старый код.

Я закончил с:

/app
/Console
/Http
etc...
/old
/lib
/screens
/config
index.php

OldController был настроен с:

/**
* Catches all routes for old code
*
* @codeCoverageIgnore
* @return \Illuminate\Http\Response
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function index($all = null)
{
ob_start();
require __DIR__.'/../../old/index.php';
$html = ob_get_clean();
return response()->string($html);
}

Старый / index.php должен был сделать что-то вроде:

// TODO: In place to avoid E_DEPRECATED errors for mysql
error_reporting(E_ALL ^ E_DEPRECATED);
session_start();

// Allow all hardcoded includes in old/ to work
$path = __DIR__;
set_include_path(get_include_path() . PATH_SEPARATOR . $path);

// Set up the global user to suit old code
if (Auth::user()) {
$myUser = Auth::user()->toArray();
$myUser['requests'] = getRequestList($myUser['uid']);
$loggedin = true;
Log::debug('using Auth::user() id ' . $myUser['uid']);
} else {
$loggedin = false;
}
// Translate Laravel flashed data to existing message arrays
if (session()->has('login_error_array')) {
$login_error_array = session()->get('login_error_array');
}

Я сделал несколько сомнительных вещей, всегда используя аутентификацию Laravel и LegacyHasher для старых паролей в AuthController:

 /**
* Login
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postLogin()
{
// Logic is a bit cleaner to follow if we fail late instead of early
if ($this->hasParams(['login_email', 'login_password'])) {
// Login using Laravel password
$credentials = [
'email' => strtolower(Input::get('login_email')),
'password' => Input::get('login_password'),
'active' => ['Y', 'B']
];

// Use the extended user provider to allow using an array of values in the credentials
$provider = new ExtendedEloquentUserProvider(new BcryptHasher(), config('auth.model'));
Auth::setProvider($provider);
if (Auth::attempt($credentials, Input::has('login_remember'))) {

Log::info(sprintf('successful login from: %s', $credentials['email']));

return redirect('/');
}

// Try with legacy password
$provider = new ExtendedEloquentUserProvider(new LegacyHasher(), config('auth.model'));
Auth::setProvider($provider);
if (Auth::attempt($credentials, Input::has('login_remember'))) {
// Save correctly hashed password
// TODO: Only add this once this is definitely working ok as it messes up the DB for legacy code
//Auth::user()->password = Hash::make(Input::get('login_password'));
//Auth::user()->save();
Log::info(
sprintf(
'legacy password for login %s, id %d',
$credentials['email'],
Auth::user()->uid
)
);

Log::info(sprintf('successful login from: %s', $credentials['email']));

return redirect('/');
}
}

Log::warning(sprintf('login failed for: %s', Input::get('login_email')));
// TODO: Use flashed errors for legacy compatibility
session()->flash('login_error_array', ['Wrong Username or Password']);

return redirect('login.htm')->withInput();
}

LegacyHasher (да, это было …)

/**
* Check the given plain value against a hash.
*
* @param  string $value
* @param  string $hashedValue
* @param  array  $options
* @return bool
*/
public function check($value, $hashedValue, array $options = array())
{
// Unused param
$options = [];
return md5($value) == $hashedValue;
}

И множество других вещей, которые выходят далеко за рамки здесь. Но это выполнимо, хоть и больно. После честной работы над этим весь старый код удаляется, и у меня есть полностью тестируемое приложение Laravel. У него несколько сотен TODO, разбросанных по всему миру, но гораздо более управляемыми.

Я рад помочь с более подробной информацией, если вы решите пойти в этом направлении.

1

Это не будет отвечать на ваш вопрос Laravel напрямую, но если подумать немного нестандартно, то в принципе вы пытаетесь достичь SSO (единый вход).

Только ради миграции это может быть излишним решением, но если вы планируете переносить больше взаимосвязанных веб-сайтов или разрабатывать приложения, стоит подумать.

Есть три варианта (как обычно):

  • реализовать ваше решение
  • использовать библиотеку например bshaffer / oauth2-сервер PHP
  • использовать сторонний сервис

Если речь идет о сторонней библиотеке, я могу порекомендовать сервис Auth0, который может снять с вас бремя аутентификации. В зависимости от количества пользователей это может даже стать бесплатным решением. Я рекомендую это исходя из собственного опыта работы с сервисом.

Подробнее о SSO

Пример SSO Auth0

1
По вопросам рекламы [email protected]