Laravel - Lưu trữ phiên không được đặt theo yêu cầu


113

Gần đây tôi đã tạo một dự án Laravel mới và đang làm theo hướng dẫn về Xác thực. Khi tôi truy cập tuyến đường đăng nhập hoặc đăng ký của mình, tôi gặp lỗi sau:

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

Tôi chưa chỉnh sửa bất kỳ tệp Laravel cốt lõi nào, tôi chỉ tạo các khung nhìn và thêm các tuyến đường vào tệp route.php của mình

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

Tôi không có nhiều kinh nghiệm với Laravel, vì vậy tôi xin lỗi vì sự thiếu hiểu biết của tôi. Tôi biết rằng có một câu hỏi khác cũng hỏi điều này, nhưng dường như không có câu trả lời nào phù hợp với tôi. Cảm ơn vì đã đọc!

Biên tập:

Đây là register.blade.php của tôi theo yêu cầu.

@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

mã post register.blade.php
Chaudhry Waqas

bạn cũng có thể thay thế các routes.php trên với chỉRoute::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas

và bạn có các tuyến đường trùng tên, sai rồi, chúng phải có tên khác
xAoc

@Adamnick Đã đăng và sẽ thử thay thế nó.
mattrick

Cấu hình trình điều khiển phiên của bạn được thiết lập như thế nào?
kipzes

Câu trả lời:


163

Bạn sẽ cần sử dụng phần mềm trung gian web nếu bạn cần trạng thái phiên, bảo vệ CSRF, v.v.

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

2
Tôi thực sự có điều đó, tôi chỉ bao gồm các tuyến đường liên quan.
mattrick

Ah, tôi hiểu ý bạn rồi, tôi đã di chuyển các tuyến đường bên trong và nó hoạt động. Cảm ơn bạn rất nhiều!
mattrick

@mattrick: hi metrix gặp lỗi tương tự. Bạn có thể giải thích nơi bạn đã di chuyển các tuyến đường bên trong phần mềm trung gian nhưng nó hiển thị lỗi "Không tìm thấy mã hóa được hỗ trợ. Mật mã".
Vipin Singh

1
@ErVipinSingh bạn sẽ cần đặt khóa 32 ký tự trong cấu hình ứng dụng của mình. Hoặc sử dụngphp artisan key:generate
Cas Bloem

2
Điều gì sẽ xảy ra nếu lộ trình đăng nhập của bạn nằm trong API?
Jay Bienvenu

56

Nếu thêm vào routesbên trong của bạn web middlewarekhông hoạt động vì bất kỳ lý do gì, hãy thử thêm cái này $middlewarevàoKernel.php

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

4
Chết tiệt, điều này đã làm việc cho tôi, nhưng tôi không hài lòng vì đó là một "sửa chữa", không phải là một giải pháp. Dẫu sao cũng xin cảm ơn!
Rav

1
CÁi này đã sửa nó giúp tôi. Cảm ơn bạn @Waiyi
Josh

1
Giải pháp của bạn khắc phục sự cố của tôi @Waiyl_Karim
Bipul Roy

Điều này đã làm việc cho tôi. Tôi đang sử dụng giao diện người dùng phản ứng nên nhóm định tuyến không hoạt động vì tôi đang sử dụng bộ định tuyến phản ứng cho các tuyến đường.
techcyclist

44

Trong trường hợp của tôi (sử dụng Laravel 5.3) chỉ thêm 2 phần mềm trung gian sau cho phép tôi truy cập dữ liệu phiên trong các tuyến API của mình:

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

Khai báo toàn bộ ( $middlewareGroupstrong Kernel.php):

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

21

Nếu câu trả lời của Cas Bloem không áp dụng (nghĩa là bạn chắc chắn đã có webphần mềm trung gian trên lộ trình áp dụng), bạn có thể muốn kiểm tra thứ tự của phần mềm trung gian trong Nhân HTTP của mình.

Thứ tự mặc định Kernel.phplà sau:

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

Lưu ý rằng VerifyCsrfTokenđến sau StartSession. Nếu bạn có những thứ này theo một thứ tự khác, sự phụ thuộc giữa chúng cũng có thể dẫn đến Session store not set on request.ngoại lệ.


tôi có nó chính xác như vậy. í vẫn nhận được tin nhắn. Tôi cũng đã thử đặt StartSession và ShareErrorsFromSession trong mảng $ middleware. St decor / frameword cũng có thể ghi được. (Tôi đang sử dụng Wampserver 3 btw.)
Meddie

sử dụng 'middleware' => [ 'web', 'youanother.log'],
Kamaro Lambert

3
Vâng! Tôi ngớ người và nghĩ rằng mình sẽ sắp xếp lại những thứ đó theo thứ tự bảng chữ cái (vì OCD) và điều đó đã phá vỡ ứng dụng. Thật không may, tôi đã không kiểm tra cho đến ngày hôm sau, đó là lý do tại sao tôi kết thúc ở đây. Chỉ đối với bản ghi, thứ tự mặc định cho nhóm phần mềm trung gian "web" trong 5.3 là: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, ReplaceBindings, VerifyCsrfToken.
Ixalmida

19

Một vấn đề có thể là bạn cố gắng truy cập vào phiên của bạn bên trong __constructor()chức năng của bộ điều khiển .

Từ Laravel 5.3+, điều này không thể thực hiện được nữa vì dù sao nó cũng không nhằm mục đích hoạt động, như đã nêu trong hướng dẫn nâng cấp .

Trong các phiên bản trước của Laravel, bạn có thể truy cập các biến phiên hoặc người dùng được xác thực trong hàm tạo của bộ điều khiển của bạn. Đây không bao giờ được coi là một tính năng rõ ràng của khuôn khổ. Trong Laravel 5.3, bạn không thể truy cập phiên hoặc người dùng được xác thực trong phương thức khởi tạo của bộ điều khiển vì phần mềm trung gian chưa chạy.

Để biết thêm thông tin cơ bản, hãy đọc phản hồi của Taylor .

Cách giải quyết

Nếu bạn vẫn muốn sử dụng điều này, bạn có thể tạo động một phần mềm trung gian và chạy nó trong phương thức khởi tạo, như được mô tả trong hướng dẫn nâng cấp:

Thay vào đó, bạn có thể xác định một phần mềm trung gian dựa trên Closure trực tiếp trong hàm tạo của bộ điều khiển. Trước khi sử dụng tính năng này, hãy đảm bảo rằng ứng dụng của bạn đang chạy Laravel 5.3.4 trở lên:

<?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
Cảm ơn bạn đã giải thích điểm __constructor (). Xóa các khái niệm của tôi.
Ashish Choudhary

16

Laravel [5,4]

Giải pháp của tôi là sử dụng trình trợ giúp phiên toàn cầu: session ()

Chức năng của nó khó hơn một chút so với $ request-> session () .

viết :

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

đẩy :

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

lấy lại :

session('key');

Đây không phải là làm việc khi chúng tôi viết biến session trong một bộ điều khiển và sử dụng trong điều khiển khác :(
Kamlesh

4

Trong trường hợp của tôi, tôi đã thêm 4 dòng sau vào $ middlewareGroups (trong 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',
],

QUAN TRỌNG: 4 dòng mới phải được thêm TRƯỚC 'ga' và 'ràng buộc'!

Nếu không, lỗi "Mã thông báo CSRF không khớp" sẽ tăng lên. Tôi đã vật lộn trong việc này trong vài giờ chỉ để tìm ra thứ tự là quan trọng.

Điều này cho phép tôi truy cập phiên trong API của mình. Tôi cũng đã thêm VerifyCsrfToken vì khi cookie / phiên có liên quan, CSRF cần được quan tâm.


Nếu bạn đang viết apis với laravel thì đây là câu trả lời mà bạn đang tìm kiếm :) hoặc thêm -> stateless () -> redirect ()
Bobby Axe

2

Bạn có thể sử dụng ->stateless()trước khi ->redirect(). Sau đó, bạn không cần phiên nữa.


0

trong trường hợp của tôi nó chỉ là để trả lại; ở cuối chức năng mà tôi đã đặt phiên


0

Nếu bạn đang sử dụng CSRF, hãy nhập 'before'=>'csrf'

Trong trường hợp của bạn Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);

Để biết thêm chi tiết, hãy xem Tài liệu Laravel 5 Các tuyến bảo vệ an ninh


0

Nó không có trong tài liệu laravel, tôi đã mất một giờ để đạt được điều này:

Phiên của tôi không tiếp tục cho đến khi tôi sử dụng phương pháp "lưu" ...

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

0

Nhóm phần mềm trung gian web Laravel 5.3+ được RouteServiceProvider tự động áp dụng cho tệp route / web.php của bạn.

Trừ khi bạn sửa đổi mảng kernel $ middlewareGroups theo thứ tự không được hỗ trợ, có thể bạn đang cố gắng đưa các yêu cầu vào như một sự phụ thuộc thông thường từ hàm tạo.

Sử dụng yêu cầu như

public function show(Request $request){

}

thay vì

public function __construct(Request $request){

}

0

Tôi đã gặp lỗi này với Laravel Sanctum. Tôi đã sửa nó bằng cách thêm \Illuminate\Session\Middleware\StartSession::class,vào apinhóm phần mềm trung gian trong Kernel.php, nhưng sau đó tôi phát hiện ra điều này "hoạt động" vì các tuyến xác thực của tôi đã được thêm vào api.phpthay vì web.php, vì vậy Laravel đã sử dụng sai bảo vệ xác thực.

Tôi đã chuyển các tuyến đường này vào đây web.phpvà sau đó chúng bắt đầu hoạt động bình thường với AuthenticatesUsers.phpđặc điểm:

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

Tôi đã tìm ra vấn đề sau khi tôi gặp một lỗi kỳ lạ khác về việc RequestGuard::logout()không tồn tại.

Nó khiến tôi nhận ra rằng các tuyến xác thực tùy chỉnh của tôi đang gọi các phương thức từ đặc điểm AuthenticatesUsers, nhưng tôi đã không sử dụng Auth::routes()để thực hiện nó. Sau đó, tôi nhận ra rằng Laravel sử dụng trình bảo vệ web theo mặc định và điều đó có nghĩa là các tuyến đường phải ở trong routes/web.php.

Đây là những gì cài đặt của tôi bây giờ trông như thế nào với Sanctum và một ứng dụng Vue SPA tách rời:

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

Lưu ý: Với Laravel Sanctum và Vue SPA cùng miền, bạn sử dụng httpOnly cookie cho cookie phiên và cookie của tôi, và cookie không an toàn cho CSRF, vì vậy bạn sử dụng webbảo vệ cho auth và mọi tuyến trả về JSON được bảo vệ khác nên sử dụng auth:sanctumphần mềm trung gian.

config / auth.php

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

...

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

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

Sau đó, bạn có thể có kiểm tra đơn vị như thế này, nơi mà giới phê bình, Auth::check(), Auth::user(), và Auth::logout()làm việc như mong đợi với cấu hình tối thiểu và sử dụng tối đa của AuthenticatesUsersRegistersUsersđặc điểm.

Dưới đây là một số thử nghiệm đơn vị đăng nhập của tôi:

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();
}

Tôi đã ghi đè các registeredauthenticatedphương thức trong các đặc điểm xác thực Laravel để chúng trả về đối tượng người dùng thay vì chỉ 204 TÙY CHỌN:

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

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

Xem mã nhà cung cấp để biết các đặc điểm xác thực. Bạn có thể sử dụng chúng một cách nguyên vẹn, cộng với hai phương pháp trên.

  • nhà cung cấp / laravel / ui / auth-backend / RegistersUsers.php
  • nhà cung cấp / laravel / ui / auth-backend / AuthenticatesUsers.php

Đây là hành động Vuex của Vue SPA của tôi để đăng nhập:

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

Tôi đã mất hơn một tuần để kiểm tra đơn vị xác thực của Laravel Sanctum + Vue SPA + cùng miền đều hoạt động theo tiêu chuẩn của tôi, vì vậy, hy vọng câu trả lời của tôi ở đây có thể giúp tiết kiệm thời gian cho người khác trong tương lai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.