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


113

我最近创建了一个新的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文件,我只创建了视图并将路由添加到了route.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上没有太多经验,所以请原谅我的无知。我知道还有另一个问题要问同样的事情,但是似乎没有一个答案对我有用。谢谢阅读!

编辑:

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

post register.blade.php代码发布
Chaudhry Waqas

您也可以仅用Route::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas

并且您的路线具有相同的名称,这是错误的,它们应该具有不同的名称
xAoc

@Adamnick发表,将尝试替换它。
mattrick

会话驱动程序配置如何设置?
kipzes 2015年

Answers:


164

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

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

2
我确实有,我只是在列出相关路线。
mattrick

啊,我明白您的意思了,我将路线移到了里面,并成功了。非常感谢!
mattrick

@mattrick:您好,metrix遇到相同的错误。您能解释一下您在中间件内部移动路由的位置,但显示错误“找不到支持的加密器。密码”。
维平·辛格

1
@ErVipinSingh,您需要在应用程序配置中设置一个32个字符的密钥。或使用php artisan key:generate
Cas Bloem

2
如果您的登录路径在API中怎么办?
周杰伦·比文维

56

如果将您的routes内部添加web middleware由于某种原因不起作用,请尝试将其添加$middlewareKernel.php

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

4
该死,这对我有用,但是我不高兴这是一个“解决办法”,而不是解决方案。不管怎么说,还是要谢谢你!
拉夫

1
这为我解决了。谢谢@Waiyi
Josh

1
您的解决方案解决了我的问题@Waiyl_Karim
Bipul Roy '18

这对我有用。我使用的是React前端,所以路由组不起作用,因为我使用的是React Router。
techcyclist's

44

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

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

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

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

21

如果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 btw。)
Meddie

使用'middleware'=> ['web','youanother.log'],
Kamaro Lambert

3
是的 我很傻,以为我会按字母顺序重新排序(因为OCD),这破坏了应用程序。不幸的是,我直到第二天才进行测试,这就是为什么我在这里结束。仅作记录,5.3中“ Web”中间件组的默认顺序为:EncryptCookies,AddQueuedCookiesToResponse,StartSession,ShareErrorsFromSession,SubstituteBindings,VerifyCsrfToken。
Ixalmida '16

19

问题可能是您尝试在控制器__constructor()功能内访问会话 。

从Laravel 5.3+开始,这已不再可能,因为它无论如何都无法正常工作,如升级指南中所述

在以前的Laravel版本中,您可以在控制器的构造函数中访问会话变量或经过身份验证的用户。从来没有打算将其作为框架的显式功能。在Laravel 5.3中,由于中间件尚未运行,因此无法在控制器的构造函数中访问会话或经过身份验证的用户。

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

解决方法

如果仍要使用此功能,则可以动态创建中间件并在构造函数中运行它,如升级指南中所述:

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

1
感谢您解释__constructor()点。清除了我的概念。
Ashish Choudhary

16

Laravel [5.4]

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

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

写作

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

推动

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

正在检索

session('key');

当我们在一个控制器中写入会话变量并在另一个控制器中使用时,这不起作用:(
Kamlesh

4

就我而言,我在$ 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编写api,这就是您想要的答案:)或添加-> stateless()-> redirect()
Bobby Ax




0

它不在laravel文档中,我花了一个小时才实现:

直到我使用“保存”方法,我的会话才持续进行...

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

0

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

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

将请求用作

public function show(Request $request){

}

代替

public function __construct(Request $request){

}

0

我在Laravel Sanctum遇到此错误。我通过将其添加\Illuminate\Session\Middleware\StartSession::class,apiKernel.php 的中间件组中来对其进行了修复,但是后来我发现这是“可行的”,因为添加了我的身份验证路由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默认使用web防护,这意味着路由应该在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特性中重写了registeredand 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,
    ]);
}

查看供应商代码以获取auth特性。您可以原封不动地使用它们,以及上面的两种方法。

  • 供应商/laravel/ui/auth-backend/RegistersUsers.php
  • 供应商/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 +身份验证单元测试,所有测试都达到了我的标准,因此希望我在这里得到的答案可以帮助将来节省其他人的时间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.