Laravel - Sklep sesji nie jest ustawiony na żądanie

114

Niedawno stworzyłem nowy projekt Laravel i postępowałem zgodnie z przewodnikiem dotyczącym uwierzytelniania. Kiedy odwiedzam moją trasę logowania lub rejestracji, pojawia się następujący błąd:

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

Nie edytowałem żadnych podstawowych plików Laravel, stworzyłem tylko widoki i dodałem trasy do mojego pliku tours.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']);

Nie mam dużego doświadczenia z Laravelem, więc wybacz mi moją ignorancję. Zdaję sobie sprawę, że jest jeszcze jedno pytanie, które zadaje to samo, ale żadna z odpowiedzi nie wydaje mi się skuteczna. Dziękuje za przeczytanie!

Edytować:

Oto mój register.blade.php zgodnie z żądaniem.

@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
mattrick
źródło
post register.blade.php code
Chaudhry Waqas
możesz również zamienić powyższe route.php na po prostuRoute::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas
a masz trasy o tej samej nazwie, to źle, powinny mieć różne nazwy
xAoc
@Adamnick Opublikowany i spróbuje go zastąpić.
mattrick
Jak jest ustawiona konfiguracja sterownika sesji?
kipzes

Odpowiedzi:

165

Jeśli potrzebujesz stanu sesji, ochrony CSRF i nie tylko, musisz użyć internetowego oprogramowania pośredniczącego .

Route::group(['middleware' => ['web']], function () {
    // your routes here
});
Cas Bloem
źródło
2
Właściwie to mam, uwzględniłem tylko odpowiednie trasy.
mattrick
Ach, teraz rozumiem, co masz na myśli, przesunąłem trasy do środka i zadziałało. Dziękuję bardzo!
mattrick
@mattrick: cześć metrix otrzymuje ten sam błąd. Czy możesz wyjaśnić, gdzie przesunąłeś trasy w oprogramowaniu pośrednim, ale pokazuje błąd „Nie znaleziono obsługiwanego modułu szyfrującego.
Vipin Singh
1
@ErVipinSingh będziesz musiał ustawić 32-znakowy klucz w konfiguracji aplikacji. Lub użyjphp artisan key:generate
Cas Bloem
2
A jeśli Twoja trasa logowania znajduje się w interfejsie API?
Jay Bienvenu
56

W przypadku dodawania routeswewnątrz web middlewarenie działa dla jakiegokolwiek powodu następnie spróbuj dodać to $middlewaredoKernel.php

protected $middleware = [
        //...
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
Waiyl Karim
źródło
4
Cholera, to zadziałało, ale nie jestem zadowolony, że to „poprawka”, a nie rozwiązanie. W każdym razie dzięki!
Rav
1
To naprawiło to dla mnie. Dziękuję @Waiyi
Josh
1
Twoje rozwiązanie rozwiązuje mój problem @Waiyl_Karim
Bipul Roy
To zadziałało dla mnie. Używam interfejsu reagującego, więc grupa tras nie działa, ponieważ używam routera reagującego do tras.
techcyclist
44

W moim przypadku (używając Laravel 5.3) dodanie tylko następującego 2 oprogramowania pośredniego umożliwiło mi dostęp do danych sesji w moich trasach API:

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

Cała deklaracja ( $middlewareGroupsw Kernel.php):

'api' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Session\Middleware\StartSession::class,
            'throttle:60,1',
            'bindings',
        ],
George Kagan
źródło
21

Jeśli odpowiedź Cas Bloem nie ma zastosowania (tj. Na pewno masz weboprogramowanie pośrednie na odpowiedniej trasie), możesz chcieć sprawdzić kolejność oprogramowania pośredniego w jądrze HTTP.

Domyślna kolejność Kernel.phpjest następująca:

$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,
    ],
];

Zauważ, że VerifyCsrfTokennastąpi później StartSession. Jeśli masz je w innej kolejności, zależność między nimi może również prowadzić do Session store not set on request.wyjątku.

Hugh Grigg 葛 修 远
źródło
mam to dokładnie tak. Nadal rozumiem wiadomość. Próbowałem też umieścić StartSession i ShareErrorsFromSession w tablicy $ middleware. Storate / frameword jest również zapisywalne. (Przy okazji używam Wampserver 3)
Meddie
użyj „middleware” => ['web', 'youanother.log'],
Kamaro Lambert
3
Tak! Byłem głupi i pomyślałem, że zmienię kolejność alfabetyczną (ponieważ OCD) i to zepsuło aplikację. Niestety nie testowałem do następnego dnia, dlatego tu trafiłem. Tylko dla rekordu, domyślna kolejność dla grupy oprogramowania pośredniego „web” w 5.3 to: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, SubstituteBindings, VerifyCsrfToken.
Ixalmida,
19

Problem może polegać na tym, że próbujesz uzyskać dostęp do sesji w ramach __constructor()funkcji kontrolera .

Od wersji Laravel 5.3+ nie jest to już możliwe, ponieważ i tak nie jest przeznaczone do pracy, jak stwierdzono w przewodniku aktualizacji .

W poprzednich wersjach Laravel można było uzyskać dostęp do zmiennych sesji lub uwierzytelnionego użytkownika w konstruktorze kontrolera. To nigdy nie miało być wyraźną cechą frameworka. W Laravel 5.3 nie możesz uzyskać dostępu do sesji lub uwierzytelnionego użytkownika w konstruktorze kontrolera, ponieważ oprogramowanie pośredniczące jeszcze nie zostało uruchomione.

Aby uzyskać więcej informacji ogólnych, przeczytaj również jego odpowiedź Taylora .

Obejście problemu

Jeśli nadal chcesz z tego korzystać, możesz dynamicznie utworzyć oprogramowanie pośredniczące i uruchomić je w konstruktorze, zgodnie z opisem w przewodniku aktualizacji:

Alternatywnie możesz zdefiniować oprogramowanie pośredniczące oparte na Closure bezpośrednio w konstruktorze kontrolera. Przed użyciem tej funkcji upewnij się, że Twoja aplikacja działa w wersji Laravel 5.3.4 lub nowszej:

<?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);
        });
    }
}
Daan
źródło
1
Dziękujemy za wyjaśnienie punktu __constructor (). Wyczyściłem moje koncepcje.
Ashish Choudhary
16

Laravel [5,4]

Moim rozwiązaniem było użycie globalnego pomocnika sesji : session ()

Jego funkcjonalność jest nieco trudniejsza niż $ request-> session () .

pisanie :

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

pchanie :

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

pobieranie :

session('key');
izzaki
źródło
To nie działa, gdy piszemy zmiennej sesji w kontrolerze i zastosowań w innym sterownikiem :(
Kamlesh
4

W moim przypadku dodałem następujące 4 wiersze do $ middlewareGroups (w 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',
],

WAŻNE: 4 nowe linki należy dodać PRZED „przepustnicą” i „wiązaniami”!

W przeciwnym razie wystąpi błąd „niezgodność tokena CSRF”. Walczyłem w tym przez kilka godzin, aby znaleźć porządek jest ważny.

To pozwoliło mi na dostęp do sesji w moim API. Dodałem również VerifyCsrfToken, ponieważ w przypadku plików cookie / sesji należy zająć się CSRF.

莊育銘
źródło
Jeśli piszesz apis z laravel, to jest odpowiedź, której szukasz :) lub dodaj -> stateless () -> redirect ()
Bobby Axe
2

Czy możesz użyć ->stateless()przed ->redirect(). Wtedy sesja nie jest już potrzebna.

Henrique Duarte
źródło
0

w moim przypadku był to po prostu powrót; na końcu funkcji, w której ustawiłem sesję

Shawinder Jit Singh
źródło
0

Jeśli używasz CSRF, wpisz 'before'=>'csrf'

W Twoim przypadku Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);

Aby uzyskać więcej informacji, zobacz Laravel 5 Documentation Security Protecting Routes

Missaka Iddamalgoda
źródło
0

Nie ma tego w dokumentacji laravela, miałem godzinę, aby to osiągnąć:

Moja sesja nie została utrzymana, dopóki nie użyłem metody „zapisz” ...

$request->session()->put('lang','en_EN');
$request->session()->save();
gtamborero
źródło
0

Grupa oprogramowania pośredniego Laravel 5.3+ jest automatycznie stosowana do pliku routingu / web.php przez dostawcę RouteServiceProvider.

Jeśli nie zmodyfikujesz tablicy jądra $ middlewareGroups w nieobsługiwanej kolejności, prawdopodobnie próbujesz wstrzyknąć żądania jako zwykłą zależność od konstruktora.

Użyj żądania jako

public function show(Request $request){

}

zamiast

public function __construct(Request $request){

}
Kanchana Randika
źródło
0

Otrzymałem ten błąd w Laravel Sanctum. Naprawiłem to, dodając \Illuminate\Session\Middleware\StartSession::class,do apigrupy oprogramowania pośredniego w Kernel.php, ale później odkryłem, że to „zadziałało”, ponieważ moje trasy uwierzytelniania zostały dodane api.phpzamiast web.php, więc Laravel używał niewłaściwego zabezpieczenia uwierzytelniania.

Przeniosłem te trasy tutaj do, web.phpa potem zaczęły działać poprawnie z AuthenticatesUsers.phpcechą:

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');

Znalazłem problem po tym, jak otrzymałem kolejny dziwny błąd dotyczący RequestGuard::logout()nie istnieje.

Uświadomiłem sobie, że moje niestandardowe trasy uwierzytelniania wywołują metody z cechy AuthenticatesUsers, ale nie Auth::routes()używałem go do tego. Potem zdałem sobie sprawę, że Laravel domyślnie używa ochrony sieci, a to oznacza, że ​​trasy powinny być włączone routes/web.php.

Tak wyglądają teraz moje ustawienia z Sanctum i odsprzężoną aplikacją Vue SPA:

Kernel.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',
    ],
];

Uwaga: w przypadku Laravel Sanctum i Vue SPA tej samej domeny używasz plików cookie httpOnly dla plików cookie sesji i plików cookie zapamiętaj mnie oraz niezabezpieczonych plików cookie dla CSRF, więc używasz webzabezpieczenia do uwierzytelniania, a każda inna chroniona droga powrotna JSON powinna używać auth:sanctumoprogramowanie pośredniczące.

config / auth.php

'defaults' => [
    'guard' => 'web',
    'passwords' => 'users',
],

...

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

Wtedy można mieć testów jednostkowych, takich jak ten, w którym krytycznie Auth::check(), Auth::user()i Auth::logout()działa zgodnie z oczekiwaniami przy minimalnej konfiguracji i maksymalnego wykorzystania AuthenticatesUsersi RegistersUserscech.

Oto kilka moich testów jednostkowych logowania:

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', '[email protected]');

    if ($user) {
        return $user;
    }

    // User::generate() is just a wrapper around User::create()
    $user = User::generate('Test User', '[email protected]', 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();
}

I overrided registeredi authenticatedmetod w cechach auth laravel tak aby przywrócić obiekt użytkownika, a nie tylko 204 opcji:

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

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

Spójrz na kod dostawcy dla cech autoryzacji. Możesz z nich korzystać bez zmian, a także z tych dwóch powyższych metod.

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

Oto moje działania Vuex do logowania w Vue SPA:

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}.`);
    }
},

Zajęło mi ponad tydzień, zanim testy jednostkowe Laravel Sanctum + tej samej domeny Vue SPA + auth działały zgodnie z moim standardem, więc mam nadzieję, że moja odpowiedź pomoże zaoszczędzić innym czas w przyszłości.

agm1984
źródło