I have a middleware like this:
<?php
namespace App\Http\Middleware;
use App\Contracts\PermissionsHandlerInterface;
use Closure;
class PermissionsHanlderMiddleware {
public $permissionsHandler;
function __construct(PermissionsHandlerInterface $permissionsHandler) {
$this -> permissionsHandler = $permissionsHandler;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next) {
$routeAction = $request->route()->getActionName();
/*
do some operations
*/
return $next($request);
}
}
but $request->route() always returns null, I think its because the router hasn't been dispatched with the request.
Note: I added my middleware to Kernal.php global middlewares to run before each request as the following
protected $middleware = [
.
.
.
'App\Http\Middleware\PermissionsHanlderMiddleware',
];
I want to get route action name before the execution of $next($request) to do some permission operations. How can i do this ?
You cannot get the route action name if the router has not yet been dispatched. The router class has not yet done its stuff - so you cannot do $router->request() - it will just be null.
If it runs as routeMiddleware as $routeMiddleware - then you can just do $router->request()
You can get the URI string in the middleware before the router has run - and do some logic there if you like: $request->segments(). i.e. that way you can see if the URI segment matches a specific route and run some code.
Edit:
One way I can quickly think of is just wrap all your routes in a group like this:
$router->group(['middleware' => 'permissionsHandler'], function() use ($router) {
// Have every single route here
});
This is the solution I did in my project:
...
public function handle($request, Closure $next) {
DB::beginTransaction();
$nextRequest = $next($request); //The router will be dispatched here, but it will reach to controller's method sometimes, so that we have to use DB transaction.
$routeName = $request->route()->getRouteName();
if ($checkPassed) {
DB::commit();
return $nextRequest;
} else {
DB::rollback();
}
}
This is also fine.
$request->path(); // path
$request->route()->getName()//name of the route
Related
I wrote a very simple middleware, like this:
class CheckToken
{
private $token='xxx';
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (! $request->tokenz == $this->token) {
return response('Unauthorized.', 401);
}
return $next($request);
}
}
Then I register it trough kernel.php, like this:
protected $routeMiddleware = [
.....
'CheckToken' => \App\Http\Middleware\CheckToken::class,
];
then Ive a very simple function in a controller guarded by this controller:
public function __construct()
{
$this->middleware('CheckToken');
}
public function push()
{
return view('home');
}
Now starts what is not clear to me:
how can i "protect" my page using this simple method?
I've tried to put this tag on the header of page but it seems to not works, maybe im in the wrong path:
<meta name="tokenz" content="xxx">
I put it even in the body but no results.
what ive misunderstood?
I believe you need to add the middleware call to the actual route:
use App\Http\Middleware\CheckAge;
Route::get('admin/profile', function () {
//
})->middleware(CheckAge::class);
This was extracted from the Laravel 5.7 documentation: Middleware - Assigning Middleware to Routes
sorry i can't create a comment. but just want to help.
does $request passed a tokenz?
you can use ?tokenz=blablabla
or you can change your method to get the tokenz
I request api to check user , and the backurl will add a query param token like this :
www.test.com?store_id=2&token = 123
I want to show this
www.test.com?store_id=2
I handle it in middleware , I wish there is a mothod to remove token before return $next($request)
but I didn't find the method. And I can't just use some method to delte this params and redirect , it will make a redirect loop.
if there is no better method, maybe I will create a new method in LoginController to remove token and redirect to where the page I from.
You can have some sort of global middleware:
class RedirectIfTokenInRequest {
public function handle($request,$next) {
if ($request->token) {
return redirect()->to(url()->current().'?'.http_build_query($request->except("token")));
}
return $next($request);
}
}
This will just redirect if there's a token parameter there. If you need to store it somehow you can use session(["token" => $request->token]); to store it before your redirect.
Middleware is the best option. You can attach middleware class, to routes, in web or to single method. My middleware proposal:
namespace App\Http\Middleware;
use Closure;
class ClearFromAttributes
{
/**
* Remove some attributes which makes some confusion.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($request->get('unwanted_param')) {
return $this->removeFromQueryAndRedirect($request, 'unwanted_param');
}
if ($request->has('second_unwanted')) {
return $this->removeFromQueryAndRedirect($request, 'second_unwanted');
}
return $next($request);
}
/**
* Remove and make redirection.
*
* #param \Illuminate\Http\Request $request
* #param string $parameter
* #return mixed
*/
public function removeFromQueryAndRedirect($request, string $parameter)
{
$request->query->remove($parameter);
return redirect()->to($request->fullUrlWithQuery([]));
}
}
Of course, I have more complicated conditions in the handle method, in reality.
Usage in controller constructor without touching Kernel file:
$this->middleware(ClearFromAttributes::class)->only('index');
This is a nice option, for single usage.
Laravel 7
You can remove parameter(s) from url by passing null to fullUrlWithQuery function like below:
request()->fullUrlWithQuery(['token ' => null])
Laravel 8 added fullUrlWithoutQuery($keys)
class RemoveParameterFromRequest
{
public function handle(Request $request, Closure $next)
{
if ($request->has('unwanted_parameter')) {
return redirect()->to($request->fullUrlWithoutQuery('unwanted_parameter'));
}
return $next($request);
}
}
I have a controller with the following in the constructor:
$this->middleware('guest', ['except' =>
[
'logout',
'auth/facebook',
'auth/facebook/callback',
'auth/facebook/unlink'
]
]);
The 'logout' rule (which is there by default) works perfectly but the other 3 rules I have added are ignored. The routes in routes.php look like this:
Route::group(['middleware' => ['web']],function(){
Route::auth();
// Facebook auth
Route::get('/auth/facebook', 'Auth\AuthController#redirectToFacebook')->name('facebook_auth');
Route::get('/auth/facebook/callback', 'Auth\AuthController#handleFacebookCallback')->name('facebook_callback');
Route::get('/auth/facebook/unlink', 'Auth\AuthController#handleFacebookUnlink')->name('facebook_unlink');
}
If I visit auth/facebook, auth/facebook/callback or auth/facebook/unlink whilst logged in I get denied by the middleware and thrown back to the homepage.
I've tried specifying the 'except' rules with proceeding /'s so they match the routes in routes.php exactly but it makes no difference. Any ideas why these rules are being ignored, whilst the default 'logout' rule is respected?
Cheers!
You need to pass the method's name instead of the URI.
<?php
namespace App\Http\Controllers;
class MyController extends Controller {
public function __construct() {
$this->middleware('guest', ['except' => [
'redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink'
]]);
}
}
Since Laravel 5.3, you can use fluent interface to define middlewares on controllers, which seems cleaner than using multidimensional arrays.
<?php
$this->middleware('guest')->except('redirectToFacebook', 'handleFacebookCallback', 'handleFacebookUnlink');
I solved this issue in my Middleware by adding this inExceptArray function. It's the same way VerifyCsrfToken handles the except array.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyMiddleware
{
/**
* Routes that should skip handle.
*
* #var array
*/
protected $except = [
'/some/route',
];
/**
* Determine if the request has a URI that should pass through.
*
* #param Request $request
* #return bool
*/
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->is($except)) {
return true;
}
}
return false;
}
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// check user authed or API Key
if (!$this->inExceptArray($request)) {
// Process middleware checks and return if failed...
if (true) {
// Middleware failed, send back response
return response()->json([
'error' => true,
'Message' => 'Failed Middleware check'
]);
}
}
// Middleware passed or in Except array
return $next($request);
}
}
If you are trying to follow the Laravel Documentation, an alternative solution to this is suggested by adding routes to the $except variable in the /Http/Middleware/VerifyCsrfToken.php file. The documentation says to add them like this:
'route/*'
But I found the only way to get it to work is by putting the routes to ignore like this:
'/route'
When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the withoutMiddleware method:
use App\Http\Middleware\CheckAge;
Route::middleware([CheckAge::class])->group(function () {
Route::get('/', function () {
//
});
Route::get('admin/profile', function () {
//
})->withoutMiddleware([CheckAge::class]);
});
for more information read documentation laravel middleware
Use this function in your Controller:
public function __construct()
{
$this->middleware(['auth' => 'verified'])->except("page_name_1", "page_name_2", "page_name_3");
}
*replace page_name_1/2/3 with yours.
For me it's working fine.
I have this solved, and here's what I am doing. Aso, I just realized this is very similar to what cmac did in his answer.
api.php
Route::group(['middleware' => 'auth'], function () {
Route::get('/user', 'Auth\UserController#me')->name('me');
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
});
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers, ThrottlesLogins;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
// ...
/**
* If the user's session is expired, the auth token is already invalidated,
* so we just return success to the client.
*
* This solves the edge case where the user clicks the Logout button as their first
* interaction in a stale session, and allows a clean redirect to the login page.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$user = $this->guard()->user();
if ($user) {
$this->guard()->logout();
JWTAuth::invalidate();
}
return response()->json(['success' => 'Logged out.'], 200);
}
}
Authenticate.php
class Authenticate extends Middleware
{
/**
* Exclude these routes from authentication check.
*
* Note: `$request->is('api/fragment*')` https://laravel.com/docs/7.x/requests
*
* #var array
*/
protected $except = [
'api/logout',
];
/**
* Ensure the user is authenticated.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route from auth check...");
return $next($request);
}
}
// code below here requires 'auth'
{ catch ($e) {
// ...
}
}
I over-engineered it slightly. Today I only need an exemption on /api/logout, but I set the logic up to quickly add more routes. If you research the VerifyCsrfToken middleware, you'll see it takes a form like this:
protected $except = [
'api/logout',
'api/foobars*',
'stripe/poop',
'https://www.external.com/yolo',
];
That's why I put that "note" in my doc above there. $request->path() === $excluded_route will probably not match api/foobars*, but $request->is('api/foobars*') should. Additionally, a person might be able to use something like $request->url() === $excluded_route to match http://www.external.com/yolo.
You should pass the function name to 'except'.
Here's an example from one of my projects:
$this->middleware('IsAdminOrSupport', ['except' => [
'ProductsByShopPage'
]
]);
This means the middleware 'IsAdminOrSupport' is applied to all methods of this controller except for the method 'ProductByShopPage'.
tried to implement a simple user register/login function on my site using the laravel default controllers (auth/password), but as soon as I login, the class RedirectIfAuthenticated handle function prevents all access to auth url's, thus I cannot logout anymore. Is there a bug and I need to write an exception on the handle function or have I missed something?
Here is how the class looks like by default:
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
//dd($next($request));
if (Auth::guard($guard)->check()) {
return redirect('/articles');
}
return $next($request);
}
}
The AuthController's constructor should look similar to this:
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
The guest middleware is handled by the RedirectIfAuthenticated class and, in order to have the logout functionality working, you should choose one:
call the logout method from your AuthController.
call whichever method you use for logout and exclude it in AuthController's constructor:
public function __construct()
{
$this->middleware('guest', ['except' => '<whichever_method>']);
}
For potentially more-advanced reasons and needs, I will show a different idea.
Inside any middleware, a person could implement their own except list. Here is a reference:
<?php
namespace App\Http\Middleware;
use Closure;
class CustomThing
protected $except = [
'api/logout',
'api/refresh',
];
public function handle($request, Closure $next)
{
foreach ($this->except as $excluded_route) {
if ($request->path() === $excluded_route) {
\Log::debug("Skipping $excluded_route in this middleware...");
return $next($request);
}
}
\Log::debug('Doing middleware stuff... '. $request->url());
}
}
I will leave it up to imagination to extend that to support other types of URLs. For example, investigate matchers such as $request->url(), $request->fullUrl(), and $request->is('admin/*').
A person could investigate the vendor code for the VerifyCsrfToken middleware and make their custom one support something like this:
protected $except = [
'api/logout',
'api/refresh',
'foo/*',
'http://www.external.com/links',
];
If you want it to be a reuseable solution, make that matching algorithm a Trait and import it into any middleware you want to exclude routes from.
When user logs in and is authenticated, I use Auth::user()->username; to show username of user on dashboard. However, for some reason when session expires the class Auth doesn't seem to work and dashboard page throws error as trying to get property of non-object for Auth::user()->username;. How can I redirect the user back to the login page when he clicks any link or refreshes the page after the session has expired?
I tried the Authenticate.php middleware but it always redirects back to login page,whatever you put the credentials either correct or incorrect.However,when I don't use this middleware it logins the user.Am I missing something?
Route.php
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
/*
Actions Handled By Resource Controller
Verb Path Action Route Name
GET /photo index photo.index
GET /photo/create create photo.create
POST /photo store photo.store
GET /photo/{photo} show photo.show
GET /photo/{photo}/edit edit photo.edit
PUT/PATCH /photo/{photo} update photo.update
DELETE /photo/{photo} destroy photo.destroy
Adding Additional Routes To Resource Controllers
If it becomes necessary to add additional routes to a resource controller beyond the default resource routes, you should define those routes before your call to Route::resource:
Route::get('photos/popular', 'PhotoController#method');
Route::resource('photos', 'PhotoController');
*/
// Display all SQL executed in Eloquent
// Event::listen('illuminate.query', function($query)
// {
// var_dump($query);
// });
define('ADMIN','admin');
define('SITE','site');
Route::group(['namespace' => ADMIN], function () {
Route::get('/','UserController#showLogin');
});
////////////////////////////////////Routes for backend///////////////////////////////////////////////////
Route::group(['prefix' => ADMIN,'middleware' => 'auth'], function () {
Route::group(['namespace' => ADMIN], function () {
//Route::get('/','EshopController#products');
//sumit routes for user registration
//Route::resource('users','UserController');
Route::get('/users/destroy/{id}','UserController#destroy');
Route::get('UserProf','UserController#userProf');
Route::get('users','UserController#index');
Route::get('/users/create','UserController#create');
Route::get('/users/adminEdit/{id}','UserController#adminEdit');
Route::post('/users/adminUpdate','UserController#adminUpdate');
Route::post('/users/store','UserController#store');
Route::get('/users/edit/{id}','UserController#edit');
Route::post('/users/update/{id}','UserController#update');
//airlines route
Route::get('airlines','AirlinesController#index');
Route::get('/airlines/create','AirlinesController#create');
Route::post('/airlines/store','AirlinesController#store');
Route::get('/airlines/edit/{id}','AirlinesController#edit');
Route::post('/airlines/update','AirlinesController#update');
Route::get('/airlines/destroy/{id}','AirlinesController#destroy');
//end sumit routes
//flight routes
Route::get('flights','FlightController#index');
Route::get('showFlightBook','FlightController#showFlightBook');
Route::get('flights/create','FlightController#create');
Route::post('flights/store','FlightController#store');
Route::get('flights/book','FlightController#book');
Route::get('flights/edit/{id}','FlightController#edit');
Route::post('flights/update','FlightController#update');
Route::get('flights/destroy/{id}','FlightController#destroy');
//Route::resource('flight','FlightController');
//hotels route
Route::get('hotels','HotelsController#index');
Route::get('/hotels/create','HotelsController#create');
Route::post('/hotels/store','HotelsController#store');
Route::get('/hotels/edit/{id}','HotelsController#edit');
Route::post('/hotels/update','HotelsController#update');
Route::get('/hotels/destroy/{id}','HotelsController#destroy');
//end sumit routes
//book-hotel routes
Route::get('hotel-book','HotelBookController#index');
Route::get('showHotelBook','HotelBookController#showHotelBook');
Route::get('hotel-book/create','HotelBookController#create');
Route::post('hotel-book/store','HotelBookController#store');
Route::get('hotel-book/book','HotelBookController#book');
Route::get('hotel-book/edit/{id}','HotelBookController#edit');
Route::post('hotel-book/update','HotelBookController#update');
Route::get('hotel-book/destroy/{id}','HotelBookController#destroy');
//Route::resource('hotel','HotelController');
//close flight routes
//for admin login
//Route::get('initlogin','UserController#lgnPage');
Route::get('login','UserController#showLogin');
// Route::get('privilegeLogin','UserController#privilegeLogin');
// Route::post('privilegeCheck','UserController#privilegeCheck');
Route::post('login','UserController#doLogin');
Route::get('/dashboard','DashController#index');
Route::get('logout','UserController#doLogout');
//user login
//Route::get('userLogin','UserController#showUserLogin');
//Route::post('userLogin','UserController#doUserLogin');
Route::get('/userDashboard','DashController#userIndex');
Route::get('Logout','UserController#doUserLogout');
//password reset
Route::get('forget-pass','UserController#showReset');
//Route::get('home', 'PassResetEmailController#index');
});
});
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Authenticate.php:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
// return redirect()->guest('auth/login');
return redirect()->guest('/');
}
}
return $next($request);
}
}
All you have to do is just put this constructor at the top of the controller for your dashboard. It seems Laravel has a middleware that handles this already. At least I can confirm from 5.4 and up.
public function __construct()
{
$this->middleware('auth');
}
If the session expires then you can redirect to log in like as
open this file app/Exceptions/Handler.php add this code
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return redirect('/login');
}
return parent::render($request, $exception);
}
If you want a middleware to be run during every HTTP request to your application, simply list the middleware class in the $middleware property of your app/Http/Kernel.php class.
So, to protect every route from being accessed without authentication do this
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\Authenticate',// add this line according to your namespace
];
it will redirect the user if not logged in. UPDATE Keep in mind that adding auth middleware as global will create redirect loop so avoid it.
Or if you want specific routes to be protected then attach the middleware auth to that route
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
I think you are not attaching the auth middleware to your routes.
Create a middleware like this
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
Then Group the routes and protect them like this
Route::group(['middleware' => 'auth'], function()
{
Route::get();
Route::get();
Route::get();
Route::get();
}
Offcourse, in the routes you have to specify your links etc, it will only allow the user when he is authenticated and if not then login page will be shown
To make session redirect to your login just add ->middleware('auth') in your router files as shown below I am using laravel 5.3
Ex:
Route::post('controllerName','folderName\fileName#fnNmae')->middleware('auth');
Or visit https://laravel.com/docs/5.3/authentication