I'm building a Laravel 4 app and I want to protect my admin area so it is only accessible if the user is logged in/authenticated.
What is the best way to do this?
The Laravel docs say you can protect a route like this:
Route::get('profile', array('before' => 'auth', function()
{
// Only authenticated users may enter...
}));
But what happens when my route looks like this:
Route::resource('cms', 'PostsController');
How do I protect a route that is directing to a controller?
Thanks in advance!
You could use Route Groups for this purpose.
So for example:
Route::group(array('before' => 'auth'), function()
{
Route::get('profile', function()
{
// Has Auth Filter
});
Route::resource('cms', 'PostsController');
// You can use Route::resource togehter with
// direct routes to the Resource Controller
// so e.g. Route::post('cms', 'PostsController#save');
});
You can put the filter on the constructor of your Controller like this:
public function __construct()
{
$this->beforeFilter('auth');
$this->beforeFilter('csrf', array('on' => 'post'));
$this->afterFilter('log', array('only' =>
array('fooAction', 'barAction')));
}
In your PostsController you can put a closure in the constructor to do the same before logic as the previous route.
public function __construct()
{
$this->beforeFilter(function()
{
//
});
}
Related
I'm experimenting with Middleware in my Laravel application. I currently have it set up to run on every route for an authenticated user, however, I want it to ignore any requests that begin with the setup URI.
Here is what my CheckOnboarding middleware method looks like:
public function handle($request, Closure $next)
{
/**
* Check to see if the user has completed the onboarding, if not redirect.
* Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
*/
if ($request->user()->onboarding_complete == false && $request->path() != 'setup') {
return redirect('setup');
} else {
return $next($request);
}
}
This is being used in my routes like this:
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
Now, if I go to /home or /account I get redirected to /setup as you would expect. This originally caused a redirect loop error hence why & $request->path() != 'setup' is in the Middleware.
I feel like this is a really clunky way of doing it, and obviously doesn't match anything after setup like the setup/settings route I have created.
Is there a better way to have this Middleware run on all routes for a user, but also set certain routes that should be exempt from this check?
There's nothing wrong with what you're doing, however, I would suggest splitting your route groups up instead i.e.:
Route::group(['middleware' => ['auth', 'checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup', 'middleware' => 'auth'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
Alternatively, have a parent group for your auth:
Route::group(['middleware' => 'auth'], function () {
Route::group(['middleware' => 'checkOnboarding'], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
This will also mean you can remove the extra condition in your middleware:
/**
* Check to see if the user has completed the onboarding, if not redirect.
* Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
*/
return $request->user()->onboarding_complete ? $next($request) : redirect('setup');
Hope this helps!
You can utilize the Controller class for this with pretty spectacular results.
If you create a __construct function inside of HTTP/Controllers/Controller.php then you can declare middleware to run on every controller action and even declare exceptions as needed.
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function __construct(){
$this->middleware('auth',['except' => ['login','setup','setupSomethingElse']]);
}
}
Be careful not to put any of the standard index, store, update, destroy functions in the exception or you'll open up potential security issues.
Since Laravel 7.7 you can use excluded_middleware like this:
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
Route::group([
'prefix' => 'setup',
'excluded_middleware' => ['checkOnboarding'],
], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
In Laravel 8.x you can also use the withoutMiddleware() method to exclude one or many route to a group middleware
Route::middleware('auth')->group(function () {
Route::get('/edit/{id}',[ProgramController::class, 'edit'])->name('edit');
Route::get('/public', [ProgramController::class, 'public'])
->name('public')->withoutMiddleware(['auth']);
});
Check also the official doc: Here
There are 2 ways to go over this problem
Try screening your routes in routes file web.php or api.php
skip routes in middleware
In case of global middleware (middleware that you want to run before all routes), you should go with skipping routes in middleware.
For example:
//add an array of routes to skip santize check
protected $openRoutes = [
'setup/*',
];
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(!in_array($request->path(), $this->openRoutes)){
//middleware code or call of function
}
return $next($request);
}
For other middleware, you can easily skip in routes file and group routes based on your middleware.
For example:
Route::group(['middleware' => 'checkOnboarding'], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
Routes on which you dont want the middleware to run , simply put them outside of the function:
//here register routes on which you dont want the middleware: checkOnboarding
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
//routes on which you want the middleware
});
I am having trouble with Laravel routes. I'm trying to redirect to a controller after some middleware in the routes. But there is always this error.
The error is:
InvalidArgumentException in UrlGenerator.php line 558: Action
App\Http\Controllers\DashboardController#index not defined.
The route code is:
Route::get('/dashboard', ['middleware' => 'auth', function() {
return Redirect::action('DashboardController#index', array('user' => \Auth::user()));
}]);
The controller:
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
return view('dashboard')->with('user', \Auth::user());
}
}
But the above code actually works (so I guess that the controller actually works):
Route::get('/testdashboard', [
'uses' => 'DashboardController#index'
]);
So what is the problem? What is a valid route action?
This is might a better way to do it, change from
Route::get('/dashboard', ['middleware' => 'auth', function() {
return Redirect::action('DashboardController#index',
array('user' => \Auth::user()));
}]);
to
Route::get('/', [
'middleware' => 'auth',
'uses' => 'DashboardController#index'
]);
This is rather a comment than a post, but I can't send it at this time. I don't undestand why do you pass parameter (\Auth:user()) to a method that doesn't require it (but it's correct when you do it for the View).
Anyways I suggest you to work on your Middleware
public function handle($request, Closure $next)
{
if (Auth::check()) {
return redirect(...);
} else {
return redirect(...);
}
}
Use this route in place of your route and upgrade your Laravel Project to Laravel 8:
Route::middleware(['auth:sanctum', 'verified'])->group(function () {
Route::get('/dashboard', 'DashboardController#index')->name('daskboard');
});
Today I tried to make some changes to my application. I tried to pass all pages through authentication first. I tried one of the answer on this site. But that didn't help. Please help. Here's my code in routes.php
<?php
Route::get('/', function(){
return view('homepage');
});
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Route::group(['before' => 'auth'], function () {
Route::get('home', function(){
return \Redirect::to('twitter');
});
Route::get('twitter', 'HomeController#index');
.
.
.
.
});
There are several routes in my file. But only twitter route works.
In laravel 5,
['before' => 'auth']
is deprecated. But instead, I should use
['middleware' => 'auth']
Laravel5 has middlewares instead of filter. I assume you are trying to show certain page to only guests and for that we have a guest middleware already built in.
Route::group(['middleware' => 'guest'], function () {
Route::get('home', function(){
return \Redirect::to('twitter');
});
Route::get('twitter', 'HomeController#index');
});
you may also use middlewares on particular functions of your controller if you want, e.g
class MyController extends Controller {
public function __construct()
{
//to add auth middleware only on update method
$this->middleware('auth', ['only' => 'update'])
//to add auth middleware on all fucntions expcept login
$this->middleware('auth', ['except' => 'login'])
}
}
So I have a route that looks like this:
Route::any('some/page', ['as' => 'some-page', 'uses' => 'SomePageController#index']);
However, I also have ajax calls at the same URL (using a request parameter called ajax like: some/page/?ajax=my_action) that I want to hit methods on my controller:
index already routes: 'SomePageController#index'
ajax = my_action needs to route: 'SomePageController#ajaxMyAction'
ajax = my_other_action needs to route: 'SomePageController#ajaxMyOtherAction'
ajax = blah_blah needs to route: 'SomePageController#ajaxBlahBlah
...
What's the elegant solution to setting this up in my routes.php file?
After inspection of Laravel's Http Request and Route classes, I found the route() and setAction() methods could be useful.
So I created a middleware to handle this:
<?php namespace App\Http\Middleware;
class Ajax {
public function handle($request, Closure $next)
{
// Looks for the value of request parameter called "ajax"
// to determine controller's method call
if ($request->ajax()) {
$routeAction = $request->route()->getAction();
$ajaxValue = studly_case($request->input("ajax"));
$routeAction['uses'] = str_replace("#index", "#ajax".$ajaxValue, $routeAction['uses']);
$routeAction['controller'] = str_replace("#index", "#ajax".$ajaxValue, $routeAction['controller']);
$request->route()->setAction($routeAction);
}
return $next($request);
}
}
Now my route looks like:
Route::any('some/page/', ['as' => 'some-page', 'middleware'=>'ajax', 'uses' => 'SomePageController#index']);
And correctly hits my controller methods (without disturbing Laravel's normal flow):
<?php namespace App\Http\Controllers;
class SomePageController extends Controller {
public function index()
{
return view('some.page.index');
}
public function ajaxMyAction(Requests\SomeFormRequest $request){
die('Do my action here!');
}
public function ajaxMyOtherAction(Requests\SomeFormRequest $request){
die('Do my other action here!');
}
...
I think this is a fairly clean solution.
You can't make this dispatch in the routing layer if you keep the same URL. You have two options :
Use different routes for your AJAX calls. For example, you can prefix all your ajax calls by /api. This is a common way :
Route::group(['prefix' => 'api'], function()
{
Route::get('items', function()
{
//
});
});
If the only different thing is your response format. You can use a condition in your controller. Laravel provides methods for that, for example :
public function index()
{
$items = ...;
if (Request::ajax()) {
return Response::json($items);
} else {
return View::make('items.index');
}
}
You can read this http://laravel.com/api/5.0/Illuminate/Http/Request.html#method_ajax and this http://laravel.com/docs/5.0/routing#route-groups if you want more details.
This is my route:
Route::group(array('before' => 'auth'), function() {
Route::resource('restaurants', 'RestaurantsController');
});
I want the restaurants.create to be available for guest users.
Is that possible?
Just declare the route outside of the Route Group like this:
Route::resource('restaurants', 'RestaurantsController');
Instead of this:
Route::group(array('before' => 'auth'), function() {
Route::resource('restaurants', 'RestaurantsController');
});
Then in your RestaurantsController controller add the before filter inside the __construct method like this:
public function __construct()
{
parent::__construct(); // If BaseController has a __construct method
$this->beforeFilter('auth', array('except' => array('create')));
}
So all the methods will have auth as the before filter but without the create method. If you need to add another before filter then you may add another one just after the first one like this:
public function __construct()
{
parent::__construct(); // If BaseController has a __construct method
// Add auth as before filter for all methods but not for create
$this->beforeFilter('auth', array('except' => array('create')));
// Only for create method
$this->beforeFilter('anotherFilter', array('only' => array('create')));
}
I believe you're looking for the guest filter, which should be included by default.
So change the line from:
Route::group(array('before' => 'auth'), function() {
to:
Route::group(array('before' => 'guest'), function() {