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, я только создал представления и добавил маршруты в свой файл 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, поэтому, пожалуйста, извините за мое незнание. Я знаю, что есть еще один вопрос, задающий то же самое, но ни один из ответов, похоже, не работает для меня. Спасибо за прочтение!

Редактировать:

Вот мой 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
Мэттрик
источник
для сообщения register.blade.php code
Чаудхри Вакас
вы также можете заменить указанный выше routes.php на justRoute::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas
а у вас есть маршруты с одинаковым именем, это неправильно, у них должны быть разные имена
xAoc
@Adamnick Разместил и попробую заменить.
Мэттрик
Как настроена конфигурация вашего драйвера сеанса?
кипзы

Ответы:

166

Вам нужно будет использовать промежуточное веб-программное обеспечение, если вам нужно состояние сеанса, защита CSRF и многое другое.

Route::group(['middleware' => ['web']], function () {
    // your routes here
});
Cas Bloem
источник
2
На самом деле он у меня есть, я просто включил соответствующие маршруты.
Мэттрик
Теперь я понимаю, о чем вы, я переместил маршруты внутрь, и это сработало. Спасибо вам большое!
Мэттрик
@mattrick: привет, metrix, появляется такая же ошибка. Можете ли вы объяснить, куда вы переместили маршруты внутри промежуточного программного обеспечения, но при этом отображается ошибка «Поддерживаемый шифровальщик не найден. Шифр».
Випин Сингх
1
@ErVipinSingh вам необходимо установить 32-символьный ключ в конфигурации вашего приложения. Или используйтеphp artisan key:generate
Cas Bloem
2
Что, если ваш маршрут входа в API?
Джей
56

Если добавление routesвнутри web middlewareне работает по какой-либо причине, попробуйте добавить это $middlewareвKernel.php

protected $middleware = [
        //...
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
Вайыл Карим
источник
4
Блин, у меня это сработало, но я не рад, что это «исправление», а не решение. Спасибо, в любом случае!
Рав
1
Это исправило это для меня. Спасибо @Waiyi
Джош
1
Ваше решение решает мою проблему @Waiyl_Karim
Бипул Рой
Это сработало для меня. Я использую реагирующий интерфейс, поэтому группа маршрутов не работает, поскольку я использую реагирующий маршрутизатор для маршрутов.
techcyclist 05
44

В моем случае (с использованием Laravel 5.3) добавление только следующих двух промежуточных программ позволило мне получить доступ к данным сеанса в моих маршрутах 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

Если ответ Каса Блума неприменим (т.е. у вас определенно есть 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.)
Meddie
use 'middleware' => ['web', 'youanother.log'],
Камаро Ламберт
3
Ага! Я был глуп и подумал, что переупорядочу их по алфавиту (из-за ОКР), и это сломало приложение. К сожалению, я не тестировал до следующего дня, поэтому и оказался здесь. Для записи: порядок по умолчанию для группы промежуточного программного обеспечения «Интернет» в версии 5.3 следующий: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, SubstituteBindings, VerifyCsrfToken.
Ixalmida
19

Проблема может заключаться в том, что вы пытаетесь получить доступ к сеансу внутри функции вашего контроллера __constructor().

Начиная с Laravel 5.3+ это больше невозможно, потому что оно не предназначено для работы, как указано в руководстве по обновлению .

В предыдущих версиях Laravel вы могли получить доступ к переменным сеанса или аутентифицированному пользователю в конструкторе вашего контроллера. Это никогда не планировалось как явная особенность фреймворка. В Laravel 5.3 вы не можете получить доступ к сеансу или аутентифицированному пользователю в конструкторе вашего контроллера, потому что промежуточное ПО еще не запущено.

Для получения дополнительной справочной информации также прочтите его ответ Тейлора .

Обходной путь

Если вы все еще хотите использовать это, вы можете динамически создать промежуточное ПО и запустить его в конструкторе, как описано в руководстве по обновлению:

В качестве альтернативы вы можете определить промежуточное ПО на основе 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 (). Очистил мои концепции.
Ашиш Чоудхари
16

Laravel [5.4]

Мое решение заключалось в использовании глобального помощника сеанса : 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 новые строки должны быть добавлены ДО 'throttle' и 'bindings'!

В противном случае возникнет ошибка «CSRF token not match». Я боролся с этим несколько часов, просто чтобы убедиться, что порядок важен.

Это позволило мне получить доступ к сеансу в моем API. Я также добавил VerifyCsrfToken, поскольку при использовании файлов cookie / сеансов необходимо позаботиться о CSRF.

莊育銘
источник
Если вы пишете apis с laravel, это ответ, который вы ищете :) или добавьте -> stateless () -> redirect ()
Bobby Axe
2

Вы можете использовать ->stateless()до ->redirect(). Тогда вам больше не нужен сеанс.

Энрике Дуарте
источник
0

в моем случае просто поставить возврат; в конце функции, где я установил сеанс

Шавиндер Джит Сингх
источник
0

Если вы используете CSRF, введите 'before'=>'csrf'

В твоем случае Route::get('auth/login', ['before'=>'csrf','uses' => 'Auth\AuthController@getLogin', 'as' => 'login']);

Для получения более подробной информации просмотрите документацию Laravel 5. Безопасность, защищающая маршруты.

Миссака Иддамалгода
источник
0

Этого нет в документации laravel, у меня был час, чтобы добиться этого:

Моя сессия не сохранялась, пока я не использовал метод «сохранить» ...

$request->session()->put('lang','en_EN');
$request->session()->save();
Gtamborero
источник
0

Группа веб-промежуточного программного обеспечения Laravel 5.3+ автоматически применяется к вашему файлу routes / web.php с помощью RouteServiceProvider.

Если вы не измените массив ядра $ 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 cookie для cookie сеанса, а также cookie запоминания меня и незащищенный cookie для 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методы в AUTH черте 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 / пользовательский интерфейс / 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 + auth с тем же доменом, которые работают в соответствии с моими стандартами, поэтому, надеюсь, мой ответ здесь поможет сэкономить время других в будущем.

agm1984
источник