强制 Laravel 使用旧身份验证登录用户

Force Laravel to log in a user using legacy authentication

我正在尝试将 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() 在所有上下文中都 return null

如何在不进行实际身份验证的情况下强制执行活动的 Laravel 用户会话?


最终结果是 Laravel 将 运行 作为遗留应用程序下的 'sub-application',同时将现有功能一一拉入,以便两者都存在一段时间在 Laravel 中实现所有功能之前的时间,它将完全取代现有的应用程序。如果尝试使用 Laravel 接管遗留身份验证比相反的方式更有意义,我对此持开放态度,但我宁愿避免必须更改底层用户 table (旧式身份验证是通过 LDAP 进行的,因此本地没有密码,没有 remember_token,但如果我 的话,添加起来很容易)。我真的只是在寻找具有最少 effort/headache.

的最短路径

您没有在 Session 上调用 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); 行,到第二个文件和 运行 它并且转储显示同一用户仍然登录。注销并删除保存的行会话将阻止它工作。希望这是您正在寻找的解决方案。

我建议考虑将 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);
}

old/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');
}

我总是在 AuthController 中使用 Laravel auth 和旧密码的 LegacyHasher,做了一些有问题的事情:

 /**
 * 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 散布在周围,但更易于管理。

如果您选择朝这个方向前进,我很乐意为您提供更多详细信息。

这有点棘手,因为 Laravel 使用由 EncryptCookies 中间件处理的加密 cookie。如果禁用它,您可以使代码正常工作,但我不推荐它。

解决方案是使用Laravel EncryptCookies 中间件解密请求,然后加密响应。这将使 Laravel.

可读由旧身份验证创建的会话

考虑这个叫做 login.php 的文件,它需要 $user_id 通过 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()的闭包里面可以读取解密后的request,也可以在这里修改session。当您 return 响应时,它将再次加密,然后您可以将其发送到浏览器。

要读取另一个文件中的会话,您只需这样做:

<?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();

这不会直接回答您的 Laravel 问题,但跳出框框思考一下,原则上您要实现的是 SSO(单点登录)。

仅出于迁移的考虑,这可能是一种矫枉过正的解决方案,但如果您打算迁移更多相互关联的网站或开发应用程序,则值得考虑。

一共有三个选项(和往常一样):

如果涉及到 3rd 方库,我可以推荐 Auth0 服务,它可以减轻您的身份验证负担。根据用户数量,它甚至可能作为免费解决方案出现。我根据自己的服务经验推荐此服务。

More about SSO

Auth0 SSO example