Laravel - Armazenamento de sessão não definido a pedido

113

Recentemente criei um novo projeto Laravel e estava seguindo o guia de Autenticação. Quando visito minha rota de login ou registro, recebo o seguinte erro:

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

Eu não editei nenhum arquivo principal do Laravel, apenas criei as visualizações e adicionei as rotas ao meu arquivo 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']);

Não tenho muita experiência com o Laravel, então desculpe minha ignorância. Estou ciente de que há outra pergunta fazendo a mesma coisa, mas nenhuma das respostas parece funcionar para mim. Obrigado por ler!

Editar:

Aqui está meu register.blade.php conforme solicitado.

@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
truque
fonte
postar código register.blade.php
Chaudhry Waqas
você também pode substituir o routes.php acima por apenasRoute::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]);
Chaudhry Waqas
e você tem rotas com o mesmo nome, está errado, eles deveriam ter nomes diferentes
xAoc
@Adamnick Postado e tentará substituí-lo.
mattrick
Como é definida a configuração do seu driver de sessão?
kipzes

Respostas:

161

Você precisará usar o middleware da web se precisar de estado de sessão, proteção CSRF e muito mais.

Route::group(['middleware' => ['web']], function () {
    // your routes here
});
Cas Bloem
fonte
2
Na verdade, eu tenho isso, só estava incluindo as rotas relevantes.
mattrick
Ah, entendo o que você quer dizer agora, mudei as rotas para dentro e funcionou. Muito obrigado!
mattrick
@mattrick: hi metrix recebendo o mesmo erro. você pode explicar para onde moveu as rotas dentro do middleware, mas mostra o erro "Nenhum criptografador suportado encontrado. A cifra".
Vipin Singh
1
@ErVipinSingh, você precisará definir uma chave de 32 caracteres na configuração do seu aplicativo. Ou usephp artisan key:generate
Cas Bloem
2
E se a sua rota de login estiver na API?
Jay Bienvenu de
56

Se adicionar o seu routesdentro do web middlewarenão trabalho por qualquer motivo, em seguida, tente adicionar isso $middlewareemKernel.php

protected $middleware = [
        //...
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
Waiyl Karim
fonte
4
Droga, isso funcionou para mim, mas não estou feliz que seja um "conserto", não uma solução. Obrigado mesmo assim!
Rav
1
Isso consertou para mim. Obrigado @Waiyi
Josh
1
Sua solução corrige meu problema @Waiyl_Karim
Bipul Roy
Isso funcionou para mim. Estou usando um frontend react, então o grupo de rotas não funciona, pois estou usando o roteador react para rotas.
técnico de
44

No meu caso (usando o Laravel 5.3) adicionar apenas os 2 seguintes middleware me permitiu acessar os dados da sessão em minhas rotas de API:

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

Declaração completa ( $middlewareGroupsem Kernel.php):

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

Se a resposta de Cas Bloem não se aplica (ou seja, você definitivamente tem o webmiddleware na rota aplicável), você pode querer verificar a ordem dos middlewares em seu kernel HTTP.

A ordem padrão Kernel.phpé esta:

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

Observe que VerifyCsrfTokenvem depois StartSession. Se você os tiver em uma ordem diferente, a dependência entre eles também pode levar à Session store not set on request.exceção.

Hugh Grigg 葛 修 远
fonte
eu tenho exatamente assim. Eu ainda entendo a mensagem. Também tentei colocar StartSession e ShareErrorsFromSession no array $ middleware. Storate / frameword também é gravável. (Estou usando o Wampserver 3 btw.)
Meddie
use 'middleware' => ['web', 'youanother.log'],
Kamaro Lambert
3
Sim! Eu fui burro e pensei em reordená-los em ordem alfabética (porque o TOC) e isso quebrou o aplicativo. Infelizmente não testei até o dia seguinte, por isso vim parar aqui. Apenas para registro, a ordem padrão para o grupo de middleware "web" no 5.3 é: EncryptCookies, AddQueuedCookiesToResponse, StartSession, ShareErrorsFromSession, SubstituteBindings, VerifyCsrfToken.
Ixalmida
19

Um problema pode ser que você tente acessar sua sessão dentro da __constructor()função de seu controlador .

A partir do Laravel 5.3+, isso não é mais possível porque não foi projetado para funcionar de qualquer maneira, conforme declarado no guia de atualização .

Nas versões anteriores do Laravel, você podia acessar variáveis ​​de sessão ou o usuário autenticado no construtor de seu controlador. Isso nunca teve a intenção de ser uma característica explícita da estrutura. No Laravel 5.3, você não pode acessar a sessão ou usuário autenticado no construtor do seu controlador porque o middleware ainda não rodou.

Para obter mais informações básicas, leia também a resposta de Taylor .

Gambiarra

Se ainda quiser usar isso, você pode criar dinamicamente um middleware e executá-lo no construtor, conforme descrito no guia de upgrade:

Como alternativa, você pode definir um middleware baseado em Closure diretamente no construtor de seu controlador. Antes de usar este recurso, certifique-se de que seu aplicativo está executando o Laravel 5.3.4 ou superior:

<?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
fonte
1
Obrigado por explicar o ponto __constructor (). Limpei meus conceitos.
Ashish Choudhary
16

Laravel [5.4]

Minha solução foi usar o helper de sessão global: session ()

Sua funcionalidade é um pouco mais difícil do que $ request-> session () .

escrevendo :

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

empurrando :

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

recuperando :

session('key');
izzaki
fonte
Isso não funciona quando gravamos a variável de sessão em um controlador e a usamos em outro controlador :(
Kamlesh
3

No meu caso, adicionei as seguintes 4 linhas a $ middlewareGroups (em 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',
],

IMPORTANTE: As 4 novas linhas devem ser adicionadas ANTES de 'throttle' e 'bindings'!

Caso contrário, aparecerá um erro "token CSRF não correspondente". Lutei nisso por várias horas só para descobrir que a ordem é importante.

Isso me permitiu acessar a sessão em minha API. Também adicionei VerifyCsrfToken, pois quando cookies / sessões estão envolvidos, CSRF precisa ser cuidado.

莊育銘
fonte
Se você está escrevendo apis com laravel, esta é a resposta que você está procurando :) ou adicione -> stateless () -> redirect ()
Bobby Axe
2

Você pode usar ->stateless()antes do ->redirect(). Então você não precisa mais da sessão.

Henrique duarte
fonte
0

no meu caso era só colocar retorno; no final da função onde eu configurei a sessão

Shawinder Jit Singh
fonte
0

Se você estiver usando CSRF, digite 'before'=>'csrf'

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

Para mais detalhes veja Laravel 5 Documentação Segurança Protegendo Rotas

Missaka Iddamalgoda
fonte
0

Não está na documentação do laravel, levei uma hora para conseguir isso:

Minha sessão não persistiu até que usei o método "salvar" ...

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

O grupo de middleware da web do Laravel 5.3+ é automaticamente aplicado ao seu arquivo routes / web.php pelo RouteServiceProvider.

A menos que você modifique o array $ middlewareGroups do kernel em uma ordem não suportada, provavelmente você está tentando injetar solicitações como uma dependência regular do construtor.

Use a solicitação como

public function show(Request $request){

}

ao invés de

public function __construct(Request $request){

}
Kanchana Randika
fonte
0

Eu estava recebendo este erro com o Laravel Sanctum. Corrigi isso adicionando \Illuminate\Session\Middleware\StartSession::class,ao apigrupo de middleware em Kernel.php, mas depois descobri que isso "funcionou" porque minhas rotas de autenticação foram adicionadas ao api.phpinvés de web.php, então o Laravel estava usando o protetor de autenticação errado.

Mudei essas rotas aqui para web.phpe então elas começaram a funcionar corretamente com a AuthenticatesUsers.phpcaracterística:

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

Eu descobri o problema depois de receber outro erro estranho sobre RequestGuard::logout()não existe.

Isso me fez perceber que minhas rotas de autenticação personalizadas são métodos de chamada do traço AuthenticatesUsers, mas eu não estava usando Auth::routes()para fazer isso. Então percebi que o Laravel usa o web guard por padrão e isso significa que as rotas devem estar ativadas routes/web.php.

É assim que minhas configurações parecem agora com Sanctum e um aplicativo Vue SPA desacoplado:

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

Nota: Com o Laravel Sanctum e Vue SPA de mesmo domínio, você usa cookies httpOnly para cookie de sessão e cookie de lembrar-me, e cookie inseguro para CSRF, então você usa a webproteção para autenticação e todas as outras rotas de retorno JSON protegidas devem usar auth:sanctummiddleware.

config / auth.php

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

...

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

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

Então você pode ter testes de unidade como esta, onde criticamente, Auth::check(), Auth::user(), e Auth::logout()funcionam como esperado com configuração mínima e uso máximo de AuthenticatesUserse RegistersUserstraços.

Aqui estão alguns dos meus testes de unidade de login:

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

Eu substituí os métodos registerede authenticatednas características de autenticação do Laravel para que eles retornem o objeto do usuário em vez de apenas as 204 OPÇÕES:

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

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

Observe o código do fornecedor para as características de autenticação. Você pode usá-los intactos, além dos dois métodos acima.

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

Aqui estão as ações do Vuex do Vue SPA para login:

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

Levei mais de uma semana para fazer com que os testes de unidade Laravel Sanctum + Vue SPA + auth de mesmo domínio funcionassem de acordo com o meu padrão, então espero que minha resposta aqui possa ajudar outros a economizar tempo no futuro.

agm1984
fonte