Laravel - 未根据请求设置会话存储


Laravel - Session store not set on request

我最近创建了一个新的Laravel项目,并遵循了身份验证指南。当我访问我的登录或注册路由时,出现以下错误:

ErrorException in Request.php line 775:
Session store not set on request. (View: C:'Users'Matthew'Documents'test'resources'views'auth'register.blade.php)

我没有编辑任何核心Laravel文件,我只创建了视图并将路线添加到我的路线.php文件中

// Authentication routes
Route::get('auth/login', ['uses' => 'Auth'AuthController@getLogin', 'as' => 'login']);
Route::post('auth/login', ['uses' => 'Auth'AuthController@postLogin', 'as' => 'login']);
Route::get('auth/logout', ['uses' => 'Auth'AuthController@getLogout', 'as' => 'logout']);
// Registration routes
Route::get('auth/register', ['uses' => 'Auth'AuthController@getRegister', 'as' => 'register']);
Route::post('auth/register', ['uses' => 'Auth'AuthController@postRegister', 'as' => 'login']);

我对拉拉维尔没有太多经验,所以请原谅我的无知。我知道还有另一个问题问同样的事情,但这两个答案似乎都不适合我。感谢您的阅读!

编辑:

这是我的注册.blade.php按照要求。

@extends('partials.main')
@section('title', 'Test | Register')
@section('content')
    <form method="POST" action="/auth/register">
        {!! csrf_field() !!}
        <div class="ui input">
          <input type="text" name="name" value="{{ old('name') }}" placeholder="Username">
        </div>
        <div class="ui input">
          <input type="email" name="email" value="{{ old('email') }}" placeholder="Email">
        </div>
        <div class="ui input">
          <input type="password" name="password" placeholder="Password">
        </div>
        <div class="ui input">
          <input type="password" name="password_confirmation"placeholder="Confirm Password">
        </div>
        <div>
            <button class="ui primary button" type="submit">Register</button>
        </div>
    </form>
@endsection

如果您需要会话状态、CSRF 保护等,则需要使用 Web 中间件

Route::group(['middleware' => ['web']], function () {
    // your routes here
});
如果将

routes添加到web middleware中由于任何原因不起作用,请尝试将其添加到$middleware Kernel.php

protected $middleware = [
        //...
        'Illuminate'Session'Middleware'StartSession::class,
        'Illuminate'View'Middleware'ShareErrorsFromSession::class,
];

就我而言(使用 Laravel 5.3(,仅添加以下 2 个中间件允许我访问 API 路由中的会话数据:

  • 'App'Http'Middleware'EncryptCookies::class
  • 'Illuminate'Session'Middleware'StartSession::class

完整声明(内核.php $middlewareGroups(:

'api' => [
            'App'Http'Middleware'EncryptCookies::class,
            'Illuminate'Session'Middleware'StartSession::class,
            'throttle:60,1',
            'bindings',
        ],

如果 Cas Bloem 的答案不适用(即您肯定在适用的路由上获得了web中间件(,您可能需要检查 HTTP 内核中中间件的顺序。

Kernel.php的默认顺序是这样的:

$middlewareGroups = [
    'web' => [
        'App'Http'Middleware'EncryptCookies::class,
        'Illuminate'Cookie'Middleware'AddQueuedCookiesToResponse::class,
        'Illuminate'Session'Middleware'StartSession::class,
        'Illuminate'View'Middleware'ShareErrorsFromSession::class,
        'App'Http'Middleware'VerifyCsrfToken::class,
    ],
];

请注意,VerifyCsrfTokenStartSession 之后。如果它们以不同的顺序排列,则它们之间的依赖关系也可能导致Session store not set on request.异常。

问题可能是您尝试在控制器的__constructor()函数中访问会话。

从Laravel5.3+开始,这不再可能,因为它无论如何都不打算工作,如升级指南中所述。

在以前版本的 Laravel 中,您可以访问会话变量或 控制器构造函数中经过身份验证的用户。这是 从未打算成为框架的显式功能。在拉拉维尔 5.3,无法访问控制器构造函数中的会话或经过身份验证的用户,因为中间件尚未运行。

有关更多背景信息,请阅读泰勒的回应。

解决方法

如果仍想使用它,可以动态创建一个中间件并在构造函数中运行它,如升级指南中所述:

作为替代方案,您可以直接定义基于闭包的中间件 在控制器的构造函数中。在使用此功能之前,请确保 您的应用程序正在运行 Laravel 5.3.4 或更高版本:

<?php
namespace App'Http'Controllers;
use App'User;
use Illuminate'Support'Facades'Auth;
use App'Http'Controllers'Controller;
class ProjectController extends Controller
{
    /**
     * All of the current user's projects.
     */
    protected $projects;
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware(function ($request, $next) {
            $this->projects = Auth::user()->projects;
            return $next($request);
        });
    }
}

Laravel[5.4]

我的解决方案是使用全局会话助手:会话((

它的功能比$request->session((有点难。

写作

session(['key'=>'value']);

推送

session()->push('key', $notification);

正在检索

session('key');

我正在使用Laravel 7.x,出现了这个问题..以下修复了它:

转到kernel.php并将这两个类添加到protected $middleware

'Illuminate'Session'Middleware'StartSession::class,
'Illuminate'View'Middleware'ShareErrorsFromSession::class,

就我而言,我在$middlewareGroups中添加了以下 4 行(在 app/Http/Kernel.php 中(:

'api' => [
    'App'Http'Middleware'EncryptCookies::class,
    'Illuminate'Cookie'Middleware'AddQueuedCookiesToResponse::class,
    'Illuminate'Session'Middleware'StartSession::class,
    'App'Http'Middleware'VerifyCsrfToken::class,
    'throttle:60,1',
    'bindings',
],

重要提示:必须在"油门"和"绑定"之前添加 4 行新行!

否则,将出现"CSRF 令牌不匹配"错误。我为此挣扎了几个小时,只是为了找到顺序很重要。

这允许我在 API 中访问会话。我还添加了VerifyCsrfToken,因为当涉及cookie/会话时,CSRF需要得到照顾。

我在Laravel Sanctum上遇到了这个错误。我通过在 Kernel.php 中的api中间件组中添加'Illuminate'Session'Middleware'StartSession::class,来修复它,但后来我发现这"有效",因为我的身份验证路由是在 api.php 而不是 web.php 中添加的,所以 Laravel 使用了错误的身份验证保护。

我把这些路线移到了web.php,然后它们开始用AuthenticatesUsers.php特征正常工作:

Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {
    Route::post('register', 'Auth'RegisterController@register')->name('register');
    Route::post('login', 'Auth'LoginController@login')->name('login');
    Route::post('password/email', 'Auth'ForgotPasswordController@sendResetLinkEmail');
    Route::post('password/reset', 'Auth'ResetPasswordController@reset');
    Route::post('email/verify/{user}', 'Auth'VerificationController@verify')->name('verification.verify');
    Route::post('email/resend', 'Auth'VerificationController@resend');
    Route::post('oauth/{driver}', 'Auth'OAuthController@redirectToProvider')->name('oauth.redirect');
    Route::get('oauth/{driver}/callback', 'Auth'OAuthController@handleProviderCallback')->name('oauth.callback');
});
Route::post('logout', 'Auth'LoginController@logout')->name('logout');

在我收到另一个关于RequestGuard::logout()不存在的奇怪错误后,我想出了问题所在。

它让我意识到我的自定义身份验证路由正在从 AuthenticatesUsers 特征调用方法,但我没有使用Auth::routes()来完成它。然后我意识到Laravel默认使用网络防护,这意味着路由应该在routes/web.php

这是我现在使用 Sanctum 和解耦的 Vue SPA 应用程序的设置:

内核.php

protected $middlewareGroups = [
    'web' => [
        'App'Http'Middleware'EncryptCookies::class,
        'Illuminate'Cookie'Middleware'AddQueuedCookiesToResponse::class,
        'Illuminate'Session'Middleware'StartSession::class,
        // 'Illuminate'Session'Middleware'AuthenticateSession::class,
        'Illuminate'View'Middleware'ShareErrorsFromSession::class,
        'App'Http'Middleware'VerifyCsrfToken::class,
        'Illuminate'Routing'Middleware'SubstituteBindings::class,
    ],
    'api' => [
        EnsureFrontendRequestsAreStateful::class,
        'Illuminate'Routing'Middleware'SubstituteBindings::class,
        'throttle:60,1',
    ],
];

注意:使用 Laravel Sanctum 和同域 Vue SPA,您使用 httpOnly cookie 进行会话 cookie,记住我 cookie,以及用于 CSRF 的不安全 cookie,因此您使用 web guard 进行身份验证,所有其他受保护的 JSON 返回路由都应使用auth:sanctum中间件。

配置/身份验证.php

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],
...
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

然后,您可以进行这样的单元测试,其中至关重要的是,Auth::check()Auth::user()Auth::logout() 按预期工作,配置最少,AuthenticatesUsersRegistersUsers特征的最大使用。

以下是我的几个登录单元测试:

测试用例.php

/**
 * Creates and/or returns the designated regular user for unit testing
 *
 * @return 'App'User
 */
public function user() : User
{
    $user = User::query()->firstWhere('email', 'test-user@example.com');
    if ($user) {
        return $user;
    }
    // User::generate() is just a wrapper around User::create()
    $user = User::generate('Test User', 'test-user@example.com', self::AUTH_PASSWORD);
    return $user;
}
/**
 * Resets AuthManager state by logging out the user from all auth guards.
 * This is used between unit tests to wipe cached auth state.
 *
 * @param array $guards
 * @return void
 */
protected function resetAuth(array $guards = null) : void
{
    $guards = $guards ?: array_keys(config('auth.guards'));
    foreach ($guards as $guard) {
        $guard = $this->app['auth']->guard($guard);
        if ($guard instanceof SessionGuard) {
            $guard->logout();
        }
    }
    $protectedProperty = new 'ReflectionProperty($this->app['auth'], 'guards');
    $protectedProperty->setAccessible(true);
    $protectedProperty->setValue($this->app['auth'], []);
}

登录测试.php

protected $auth_guard = 'web';
/** @test */
public function it_can_login()
{
    $user = $this->user();
    $this->postJson(route('login'), ['email' => $user->email, 'password' => TestCase::AUTH_PASSWORD])
        ->assertStatus(200)
        ->assertJsonStructure([
            'user' => [
                ...expectedUserFields,
            ],
        ]);
    $this->assertEquals(Auth::check(), true);
    $this->assertEquals(Auth::user()->email, $user->email);
    $this->assertAuthenticated($this->auth_guard);
    $this->assertAuthenticatedAs($user, $this->auth_guard);
    $this->resetAuth();
}
/** @test */
public function it_can_logout()
{
    $this->actingAs($this->user())
        ->postJson(route('logout'))
        ->assertStatus(204);
    $this->assertGuest($this->auth_guard);
    $this->resetAuth();
}

我覆盖了 Laravel 身份验证特征中的 registeredauthenticated 方法,以便它们返回用户对象而不仅仅是 204 个选项:

public function authenticated(Request $request, User $user)
{
    return response()->json([
        'user' => $user,
    ]);
}
protected function registered(Request $request, User $user)
{
    return response()->json([
        'user' => $user,
    ]);
}

查看供应商代码以了解身份验证特征。您可以原封不动地使用它们,再加上上述两种方法。

  • vendor/laravel/ui/auth-backend/RegistersUsers.php
  • vendor/laravel/ui/auth-backend/AuthenticatesUsers.php

这是我的 Vue SPA 的 Vuex 登录操作:

async login({ commit }, credentials) {
    try {
        const { data } = await axios.post(route('login'), {
            ...credentials,
            remember: credentials.remember || undefined,
        });
        commit(FETCH_USER_SUCCESS, { user: data.user });
        commit(LOGIN);
        return commit(CLEAR_INTENDED_URL);
    } catch (err) {
        commit(LOGOUT);
        throw new Error(`auth/login# Problem logging user in: ${err}.`);
    }
},
async logout({ commit }) {
    try {
        await axios.post(route('logout'));
        return commit(LOGOUT);
    } catch (err) {
        commit(LOGOUT);
        throw new Error(`auth/logout# Problem logging user out: ${err}.`);
    }
},

我花了一周多的时间才让 Laravel Sanctum + 同域 Vue SPA + 身份验证单元测试都达到了我的标准,所以希望我在这里的答案可以帮助将来节省其他人的时间。

我遇到了同样的错误,但这是由于我的 sanctum.php 文件中配置错误造成的。在有状态域的块中,我仍然有一个开发域而不是我的生产域。

'stateful' => explode(',', env(
        'SANCTUM_STATEFUL_DOMAINS',
        'mydomain.com')
),

它抛出了完全相同的错误。

希望对某人有所帮助。
干杯

在使用sanctum运行SPA身份验证测试时,您是否遇到此问题?

溶液:

将此行添加到tests/TestCase.php


    public function setUp(): void
    {
        parent::setUp();
        $this->withHeader('origin', config('app.url'));
    }

解释:

为了将Sanctum用于SPA身份验证,您必须将其添加到api中间件中:

'Laravel'Sanctum'Http'Middleware'EnsureFrontendRequestsAreStateful::class

EnsureFrontendRequestsAreStateful类仅在其静态方法fromFrontend返回 true 时才有效:

 public static function fromFrontend($request)
    {
        $domain = $request->headers->get('referer') ?: $request->headers->get('origin');
        if (is_null($domain)) {
            return false;
        }
        // ... ommited
}

未设置会话存储,因为当此方法在单元测试中运行时$domain为 null。

您可以在->redirect()之前使用->stateless()吗?然后,您不再需要该会话。

好的,这就是您面临的问题:您正在调用一个请求关联会话,该会话未实例化为您发出的请求。您只需要在请求上设置LaravelSession,该请求将在您希望在其上提供会话数据的请求上初始化会话。

$request->setLaravelSession(session())

在我的情况下,它只是为了把返回;在我设置会话的函数末尾<</p>

div class="answers">

如果您使用的是 CSRF,请输入'before'=>'csrf'

在您的情况下 Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth'AuthController@getLogin', 'as' => 'login']);

有关更多详细信息,请查看 Laravel 5 文档安全保护路线

它不在 laravel 文档中,我已经花了一个小时来实现这一点:

直到我使用"保存"方法,我的会话才持续存在......

$request->session()->put('lang','en_EN');
$request->session()->save();

Laravel 5.3+ Web 中间件组由 RouteServiceProvider 自动应用于您的 routes/web.php 文件。

除非您以不受支持的顺序修改内核$middlewareGroups数组,否则您可能正在尝试将请求作为构造函数的常规依赖项注入。

使用请求为

public function show(Request $request){
}

而不是

public function __construct(Request $request){
}

在我自己的情况下(在 Laravel 5.8 中(,位于 ../[mylaravelapp]/config/ 目录中的 Laravel 项目中的session.php配置文件丢失了。因此,我将丢失的文件从另一个Laravel项目复制回config目录,从而解决了问题。

只需添加顶级公共/索引.php

ob_start();

它与共享主机一起工作

<小时 />

如果不起作用,请尝试在app/Http/Middleware/TrustProxies中更改此代码.php

protected $proxies;

protected $proxies = '*';

以下内容对我有用...

在用户登录时创建会话

$r->session()->put('usrSession',$r->uName);

这里的"usrSession"是我的密钥,"uName"来自请求的变量(实际上是该用户的唯一用户名(

用户注销时的刷新(所有(会话

$r->session()->flush();

一些笔记可以帮助你,因为帮助了我:

正如这篇文章所说,dd('')功能与session功能有一些冲突(当然可能与 cookie 等其他一些功能,......我没有测试(;

对我来说,Reson是未知的! 也许dd()清除缓冲区。

所以不要dd()session结合使用。

您应该将spa的域地址添加到"有状态"部分中的config/sanctum.php文件中