【问题标题】:Laravel - Session store not set on requestLaravel - 未按要求设置会话存储
【发布时间】:2016-03-30 17:17:15
【问题描述】:

我最近创建了一个新的 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 文件,我只是创建了视图并将路由添加到我的 routes.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']);

我对 Laravel 没有太多经验,所以请原谅我的无知。我知道有another question 在问同样的事情,但似乎没有一个答案对我有用。感谢阅读!

编辑:

这是我要求的 register.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

【问题讨论】:

  • 你也可以用Route::controllers([ 'auth' =&gt; 'Auth\AuthController', 'password' =&gt; 'Auth\PasswordController', ]);替换上面的routes.php
  • 你有同名的路由,这是错误的,他们应该有不同的名字
  • 你的会话驱动配置是怎样设置的?

标签: php laravel session session-cookies


【解决方案1】:

您在使用 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 为空。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

      好的,这就是您所面临的:您正在调用一个请求关联会话,该会话未实例化为您发出的请求。您只需要在请求上设置 LaravelSession,该请求将根据您希望获得可用会话数据的请求启动会话。

      $request->setLaravelSession(session())
      

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

            我将这些路由从这里移到web.php,然后它们开始使用AuthenticatesUsers.php trait 正常工作:

            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 默认使用 web Guard,这意味着路由应该在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 保护auth 以及所有其他受保护的 JSON 返回路由都应使用 auth:sanctum 中间件。

            config/auth.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 特征的使用按预期工作。

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

            TestCase.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'], []);
            }
            

            LoginTest.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 auth 特征中的 registeredauthenticated 方法,以便它们返回用户对象,而不仅仅是 204 OPTIONS:

            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 + auth 单元测试都符合我的标准,所以希望我在这里的回答可以帮助将来节省其他人的时间。

            【讨论】:

            • 如果我想要用户的身份验证,我需要使用登录吗?我没有登录,因为 jwt 通过代理进行身份验证。我只想将会话值设置为经过身份验证,以便后续请求不需要再次与第 3 方进行身份验证。获取“未按要求设置会话存储。”..
            • 如果您提出问题并将其链接到此处,它将为其他人创建一个很好的后续参考,我也许可以提供帮助;但是自从我查看身份验证代码以来已经有一段时间了。 Session store not set on request 可能与 StartSession::class 直接相关。上游的某些东西可能没有正确设置,或者您对web 和/或api 的使用没有正确对齐。
            • 谢谢你的回答,就像一个魅力!
            【解决方案7】:

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

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

            使用请求作为

            public function show(Request $request){
            
            }
            

            而不是

            public function __construct(Request $request){
            
            }
            

            【讨论】:

              【解决方案8】:

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

              在我使用“保存”方法之前,我的会话没有持续...

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

              【讨论】:

                【解决方案9】:

                在我的例子中,我将以下 4 行添加到 $middlewareGroups(在 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',
                ],
                

                重要提示:必须在 'throttle' 和 'bindings' 之前添加 4 个新行!

                否则会出现“CSRF 令牌不匹配”错误。我为此苦苦挣扎了几个小时,只是为了发现顺序很重要。

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

                【讨论】:

                【解决方案10】:

                您可以在-&gt;redirect() 之前使用-&gt;stateless()。 那么你就不再需要会话了。

                【讨论】:

                  【解决方案11】:

                  如果您使用 CSRF,请输入 'before'=&gt;'csrf'

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

                  更多详情请查看 Laravel 5 文档Security Protecting Routes

                  【讨论】:

                    【解决方案12】:

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

                    从 Laravel 5.3+ 开始,这是不可能的,因为它无论如何都不能工作,as stated in the upgrade guide

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

                    欲了解更多背景信息,请阅读Taylor他的回复。

                    解决方法

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

                    作为替代方案,您可以直接定义基于闭包的中间件 在控制器的构造函数中。在使用此功能之前,请确保 你的应用程序正在运行 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);
                            });
                        }
                    }
                    

                    【讨论】:

                    • 这是我的问题,我试图访问 __constructor() 方法中的会话变量。
                    【解决方案13】:

                    在我的情况下,它只是把 返回 ; 在我设置会话的函数结束时

                    【讨论】:

                      【解决方案14】:

                      Laravel [5.4]

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

                      它的功能比$request->session()要难一点。

                      写作

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

                      推动

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

                      检索

                      session('key');
                      

                      【讨论】:

                      • 当我们在控制器中写入会话变量并在另一个控制器中使用时,这不起作用:(
                      【解决方案15】:

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

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

                      【讨论】:

                      • 我确实有,我只是包括相关路线。
                      • 啊,我明白你的意思了,我把路线移到了里面,它起作用了。非常感谢!
                      • @ErVipinSingh 您需要在应用配置中设置 32 个字符的密钥。或使用php artisan key:generate
                      • 我遇到了 laravel 不持久身份验证的问题。这修复了它,因为很明显没有创建 cookie。
                      • 如果您的登录路由在 API 中怎么办?
                      【解决方案16】:

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

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

                      整个声明(Kernel.php 中的$middlewareGroups):

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

                      【讨论】:

                        【解决方案17】:

                        如果在web middleware 中添加routes 因任何原因不起作用,请尝试将其添加到$middlewareKernel.php

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

                        【讨论】:

                        • 该死,这对我有用,但我不高兴这是一个“修复”,而不是解决方案。还是谢谢!
                        • 这对我有用。我正在使用 react 前端,因此路由组不起作用,因为我正在使用 react 路由器进行路由。
                        • 天哪,这确实有效。谢谢
                        【解决方案18】:

                        如果 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,
                            ],
                        ];
                        

                        请注意,VerifyCsrfToken 位于 StartSession 之后。如果您以不同的顺序获得这些,它们之间的依赖关系也可能导致Session store not set on request. 异常。

                        【讨论】:

                        • 我完全一样。 í 仍然收到消息。我还尝试将 StartSession 和 ShareErrorsFromSession 放在 $middleware 数组中。存储/帧字也是可写的。 (我正在使用 Wampserver 3 顺便说一句。)
                        • 使用'中间件' => ['web','youanother.log'],
                        • 是的!我很笨,以为我会按字母顺序重新排序(因为强迫症),这破坏了应用程序。不幸的是,我直到第二天才进行测试,这就是我最终来到这里的原因。仅作记录,5.3中“web”中间件组的默认顺序为:EncryptCookies、AddQueuedCookiesToResponse、StartSession、ShareErrorsFromSession、SubstituteBindings、VerifyCsrfToken。
                        猜你喜欢
                        • 2023-03-22
                        • 2020-08-24
                        • 2017-03-13
                        • 2017-09-30
                        • 2021-05-08
                        • 2016-08-30
                        • 2020-11-28
                        • 2018-04-12
                        • 2016-10-08
                        相关资源
                        最近更新 更多