Laravel - 未按要求设置会话存储
Posted
技术标签:
【中文标题】Laravel - 未按要求设置会话存储【英文标题】:Laravel - Session store not set on request 【发布时间】: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' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
替换上面的routes.php
你有同名的路由,这是错误的,他们应该有不同的名字
你的会话驱动配置是怎样设置的?
【参考方案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()
以最少的配置和最大的 AuthenticatesUsers
和 RegistersUsers
特征的使用按预期工作。
这是我的几个登录单元测试:
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 特征中的 registered
和 authenticated
方法,以便它们返回用户对象,而不仅仅是 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。
【讨论】:
如果您正在使用 laravel 编写 api,这就是您要寻找的答案 :) 或添加 ->stateless()->redirect()【参考方案10】:您可以在->redirect()
之前使用->stateless()
。
那么你就不再需要会话了。
【讨论】:
【参考方案11】:如果您使用 CSRF,请输入 'before'=>'csrf'
在你的情况下
Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => '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
因任何原因不起作用,请尝试将其添加到$middleware
到Kernel.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。以上是关于Laravel - 未按要求设置会话存储的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 8 Sanctum SPA Auth - 未按要求设置会话存储
php artisan route:list RuntimeException 会话存储未按请求设置