Laravel - ไม่ได้ตั้งค่าร้านค้าเซสชันตามคำขอ


114

ฉันเพิ่งสร้างโปรเจ็กต์ 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 code
Chaudhry Waqas

คุณยังสามารถแทนที่ route.php ข้างต้นได้ด้วยRoute::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas

และคุณมีเส้นทางที่ใช้ชื่อเดียวกันไม่ถูกต้องควรมีชื่อต่างกัน
xAoc

@Adamnick โพสต์แล้วจะลองแทนที่
mattrick

การกำหนดค่าโปรแกรมควบคุมเซสชันของคุณตั้งค่าอย่างไร
kipzes

คำตอบ:


166

คุณจะต้องใช้มิดเดิลแวร์ของเว็บหากคุณต้องการสถานะเซสชันการป้องกัน CSRF และอื่น ๆ

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

2
ฉันมีแบบนั้นจริงๆฉันแค่รวมเส้นทางที่เกี่ยวข้อง
mattrick

ฉันเข้าใจว่าคุณหมายถึงอะไรตอนนี้ฉันย้ายเส้นทางเข้าไปข้างในและมันได้ผล ขอบคุณมาก!
mattrick

@mattrick: hi metrix ได้รับข้อผิดพลาดเดียวกันคุณสามารถอธิบายได้ว่าคุณย้ายเส้นทางภายในมิดเดิลแวร์ไปที่ใด แต่แสดงข้อผิดพลาด "ไม่พบตัวเข้ารหัสที่รองรับการเข้ารหัส"
Vipin Singh

1
@ErVipinSingh คุณจะต้องตั้งค่าคีย์อักขระ 32 ตัวในการกำหนดค่าแอปของคุณ หรือใช้php artisan key:generate
Cas Bloem

2
จะเกิดอะไรขึ้นหากเส้นทางการเข้าสู่ระบบของคุณอยู่ใน API
Jay Bienvenu

56

หากการเพิ่มroutesภายในของคุณweb middlewareไม่ได้ผลไม่ว่าด้วยเหตุผลใดก็ตามให้ลองเพิ่มสิ่งนี้$middlewareลงในKernel.php

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

4
ให้ตายเถอะสิ่งนี้ได้ผลสำหรับฉัน แต่ฉันไม่พอใจที่มันเป็นการ "แก้ไข" ไม่ใช่วิธีแก้ปัญหา ขอบคุณต่อไป!
Rav

1
สิ่งนี้แก้ไขให้ฉัน ขอบคุณ @Waiyi
Josh

1
ทางออกของคุณแก้ไขปัญหาของฉัน @Waiyl_Karim
Bipul Roy

สิ่งนี้ได้ผลสำหรับฉัน ฉันใช้ส่วนหน้าตอบสนองดังนั้นกลุ่มเส้นทางจึงไม่ทำงานเนื่องจากฉันใช้เราเตอร์ตอบกลับสำหรับเส้นทาง
techcyclist

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 ในอาร์เรย์มิดเดิลแวร์ $ Storate / frameword สามารถเขียนได้ (ฉันใช้ Wampserver 3 btw)
Meddie

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

3
เออ! ฉันเป็นใบ้และคิดว่าฉันจะเรียงลำดับตามตัวอักษร (เพราะ OCD) และนั่นก็ทำให้แอปเสียหาย น่าเสียดายที่ฉันไม่ได้ทดสอบจนกว่าจะถึงวันรุ่งขึ้นซึ่งเป็นสาเหตุที่ทำให้ฉันมาจบที่นี่ สำหรับเร็กคอร์ดลำดับเริ่มต้นสำหรับกลุ่มมิดเดิลแวร์ "เว็บ" ใน 5.3 คือ: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, SubstituteBindings, VerifyCsrfToken
Ixalmida

19

ปัญหาอาจเกิดจากการที่คุณพยายามเข้าถึงเซสชันของคุณภายใน__constructor()ฟังก์ชันคอนโทรลเลอร์ของ คุณ

จาก Laravel 5.3+ นี้เป็นไปไม่ได้อีกต่อไปเพราะมันไม่ได้มีวัตถุประสงค์ที่จะทำงานต่อไปตามที่ระบุไว้ในคู่มือการอัปเกรด

ใน Laravel เวอร์ชันก่อนหน้าคุณสามารถเข้าถึงตัวแปรเซสชันหรือผู้ใช้ที่ได้รับการพิสูจน์ตัวตนในตัวสร้างคอนโทรลเลอร์ของคุณ สิ่งนี้ไม่เคยตั้งใจให้เป็นคุณลักษณะที่ชัดเจนของกรอบงาน ใน 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);
        });
    }
}

1
ขอขอบคุณที่อธิบายจุด __constructor () ล้างแนวคิดของฉัน
Ashish Choudhary

16

ลาราเวล [5.4]

วิธีแก้ปัญหาของฉันคือใช้ global session helper: session ()

การทำงานของมันยากกว่า$ request-> session ()เล็กน้อย

การเขียน :

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

ผลักดัน :

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

การดึงข้อมูล :

session('key');

สิ่งนี้ไม่ทำงานเมื่อเราเขียนตัวแปรเซสชันในคอนโทรลเลอร์และใช้ในคอนโทรลเลอร์อื่น :(
Kamlesh

4

ในกรณีของฉันฉันเพิ่ม 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',
],

สำคัญ: ต้องเพิ่ม 4 บรรทัดใหม่ก่อน 'เค้น' และ 'การผูก'!

มิฉะนั้นข้อผิดพลาด "โทเค็น CSRF ไม่ตรงกัน" จะเพิ่มขึ้น ฉันต่อสู้กับเรื่องนี้มาหลายชั่วโมงเพื่อให้พบว่าคำสั่งซื้อนั้นสำคัญ

สิ่งนี้ทำให้ฉันสามารถเข้าถึงเซสชันใน API ของฉันได้ ฉันยังได้เพิ่ม VerifyCsrfToken เมื่อเกี่ยวข้องกับคุกกี้ / เซสชัน CSRF จะต้องได้รับการดูแล


หากคุณกำลังเขียน apis ด้วย laravel นี่คือคำตอบที่คุณกำลังมองหา :) หรือเพิ่ม-> stateless () -> redirect ()
Bobby Axe

2

คุณสามารถใช้->stateless()ก่อนไฟล์->redirect(). จากนั้นคุณไม่ต้องการเซสชันอีกต่อไป



0

หากคุณใช้ CSRF ให้ป้อน 'before'=>'csrf'

ในกรณีของคุณ Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);

สำหรับรายละเอียดเพิ่มเติมโปรดดู Laravel 5 Documentation Security Protecting Routes


0

มันไม่ได้อยู่ในเอกสาร laravel ฉันใช้เวลาหนึ่งชั่วโมงในการบรรลุสิ่งนี้:

เซสชันของฉันไม่คงอยู่จนกว่าฉันจะใช้เมธอด "บันทึก" ...

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

0

กลุ่มมิดเดิลแวร์ของเว็บ Laravel 5.3+ จะถูกนำไปใช้กับไฟล์ route / web.php ของคุณโดยอัตโนมัติโดย RouteServiceProvider

เว้นแต่ว่าคุณจะแก้ไขอาร์เรย์ kernel $ middlewareGroups ในลำดับที่ไม่ได้รับการสนับสนุนอาจเป็นไปได้ว่าคุณกำลังพยายามที่จะฉีดคำร้องขอเป็นการอ้างอิงจากตัวสร้างปกติ

ใช้คำขอเป็น

public function show(Request $request){

}

แทน

public function __construct(Request $request){

}

0

ฉันได้รับข้อผิดพลาดนี้จาก Laravel Sanctum ฉันแก้ไขโดยเพิ่ม\Illuminate\Session\Middleware\StartSession::class,ไปยังapiกลุ่มมิดเดิลแวร์ใน Kernel.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 routes/web.phpใช้ยามเว็บโดยการเริ่มต้นและการที่เส้นทางหมายถึงควรจะอยู่ใน

นี่คือลักษณะการตั้งค่าของฉันในขณะนี้ด้วย Sanctum และแอป 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',
    ],
];

หมายเหตุ:ด้วย Laravel Sanctum และ Vue SPA โดเมนเดียวกันคุณจะใช้ httpOnly cookies สำหรับคุกกี้เซสชันและจดจำคุกกี้ของฉันและคุกกี้ที่ไม่ปลอดภัยสำหรับ CSRF ดังนั้นคุณควรใช้การwebป้องกันเพื่อรับรองความถูกต้องและทุก ๆ เส้นทางที่ได้รับการป้องกันอื่น ๆ ควรใช้เส้นทางการส่งคืน 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();
}

ฉันลบล้างregisteredและauthenticatedวิธีการในลักษณะการตรวจสอบสิทธิ์ Laravel เพื่อให้พวกเขาส่งคืนวัตถุผู้ใช้แทนที่จะเป็นเพียง 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,
    ]);
}

ดูรหัสผู้ขายสำหรับลักษณะการตรวจสอบสิทธิ์ คุณสามารถใช้โดยไม่แตะต้องรวมทั้งสองวิธีข้างต้น

  • ผู้ขาย / laravel / ui / auth-backend / RegistersUsers.php
  • ผู้ขาย / laravel / ui / auth-backend / AuthenticatesUsers.php

นี่คือการดำเนินการ Vuex ของ 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}.`);
    }
},

ฉันใช้เวลากว่าหนึ่งสัปดาห์ในการรับ Laravel Sanctum + การทดสอบหน่วย Vue SPA + โดเมนเดียวกันทั้งหมดทำงานได้ตามมาตรฐานของฉันดังนั้นหวังว่าคำตอบของฉันที่นี่จะช่วยประหยัดเวลาของผู้อื่นในอนาคต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.