简体   繁体   中英

How to password protect a page with Laravel?

I'm trying to set up some page in Laravel that require a password to view.

A page is a model, called Page.

Each page has an associated password, stored in the Pages database table.

The schema

Schema::create('pages', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('name');
    $table->string('client_id');
    $table->string('url');
    $table->text('content');
    $table->string('password');
    $table->timestamps();
});

I have a route, eg Route::get('/page/{url}', 'PageController@index')->middleware('gate'); which will show the 'page', it's just a blade/vue file with specific information injected into the template. These pages allow the user to upload files via AJAX.

Currently, I created some middleware to actual authentication part.

Middleware

public function handle($request, Closure $next)
{
    if(Cookie::get($request->route('url'))){
        return $next($request);
    }
    session(['page' => $request->route('url')]);
    return redirect()->route('gate',['page'=> $request->route('url')]);
}

PublicController.php

public function gate_check(Request $request)
{
  //this part just ensures the correct page is loaded after being authed
  $past = parse_url($request->session()->get('_previous')['url']);

  if(array_key_exists('query', $past)){
    $page = str_replace('page=', '',parse_url($request->session()->get('_previous')['url'])['query']);

    $stored_password = Page::where('url', $page)->first()->password;
    $password = $request->password;  

    if($password === $stored_password){
      //if password matches, then redirect to that page
      return redirect()->route('page', session('page'));
    } else {
      //else redirect them back to the login page to try again
      return back();
    }
  } else {
    //if there is no page information that use should go to, just send them to google instead
    return redirect('https://google.com');
  }

}

The idea of the middleware/auth method was to redirect the user to a login page if they weren't authed. This login page consists only of a password that you need to enter.

Once they enter it, I set a cookie so that they can bypass having to re-login again.

I now realise that is is insecure, or at least it seems that way as the expiry time of the cookie can be manipulated by the client/user - resulting in them being able to stay logged in forever.

Just wish to re-iterate that the method described above is working, but it is insecure. I also should re-iterate that these 'pages' allow users to upload files via ajax. And only should the user be allowed to upload if they're on that specific page eg by CSRF.

I need a secure way to password protect pages, which the expiry time of the 'session' can be customised. I'd also need a way to 'refresh' or 'extend' the active session without a page refresh using AJAX so that the user can stay on the upload page (in case uploads take a long time).

Standard user account with username/email & password is not applicable. Password only.

Wordpress has this feature built in - why is it so hard to do with Laravel for what seems so trivial?

How would you approach this?

This is how I would have done:

Standard user account with username/email & password is not applicable. Password only.

For non standard user account, you may yourself generate a guest id on every correct password entered and store it to session.

Schema::create('active_guests', function (Blueprint $table) {
$table->bigIncrements('guest_id'); # on your model specify this as primary key
$table->unsignedBigInteger('page_id'); # authorized for after validating password
$table->integer('expires_at'); # store it as unixtimestamp now()->addHours(5)->timestamp
$table->timestamps();
});

You can just query this way to check authentication

$page = Page::where('url', $page)->where('password', $password)->first();
if ($page) {
ActiveGuest::updateOrcreate(....);
Session::get('pages_authorized_for', [array_of_pages]);
// push the new page id and store it back to session as array
} else {
// incorrect password or page removed
}

Just wish to re-iterate that the method described above is working, but it is insecure. I also should re-iterate that these 'pages' allow users to upload files via ajax. And only should the user be allowed to upload if they're on that specific page eg by CSRF.

I need a secure way to password protect pages, which the expiry time of the 'session' can be customised. I'd also need a way to 'refresh' or 'extend' the active session without a page refresh using AJAX so that the user can stay on the upload page (in case uploads take a long time).

From your ajax you can just increment the expiry time on active_guests table.

For longer upload time to be extended you may add a last_activity as timestamp.

Writing your own authentication solution is hard. Is it possible to reconsider the strategy for securing the pages?

If you able to follow the normal user based authentication strategy then you could utilise the authentication services which Laravel provides and it may also provide other advantages by removing edge cases you may encounter.

Depending on the number of pages you have to authenticate, a boolean column for each page could be added to the User table or you could add a "pages" column of type sting which holds a list of pages this user has access to.

The Laravel Authorization Policies ( https://laravel.com/docs/6.x/authorization#creating-policies ) can then be used to enforce access to the page and within Blade you can use the policy to build the dynamic list of pages available to the user ( https://laravel.com/docs/6.x/authorization#via-blade-templates )

Other benefits: - Avoids sharing a common password for each page. You may not know if the password has been shared outside of the authorised group of users and changing it will impact all users of the page.

  • If a user forgets a password then they can utilise the standard Password Reset process.

  • User does not need to track multiple passwords if they have access to multiple pages and Password managers struggle when there are multiple passwords for a domain.

  • Knowing which pages a user has access to can assist usability by providing a homepage that lists all the pages available to the specific user.

Use session instead of cookies. Changes for Middleware :

public function handle($request, Closure $next)
{   

    if(in_array($request->route('url'), session('pages',[]))){
       return $next($request);
    }

    session(["current-page"=>$request->route('url')]);
    return redirect()->route('gate');
}

Changes for Controller :

public function gate_check(Request $request)
{
    if(session()->has("current-page")){

        $stored_password = Page::where('url', session("current-page"))
           ->first()->password;

        if($request->password === $stored_password){
            $pages = session("pages",[]);
            array_push($pages, session("current-page"));
            session(["pages"=> $pages]);
            //if password matches, then redirect to that page
            return redirect()->route('page', ["url" => session("current-page")]);
        } else {
            //else redirect them back to the login page to try again
            return back();
        }
    } else {
        //if there is no page information that use should go to, just send them 
        // to google instead
        return redirect('https://google.com');
    }
}

You can use hashed password to save in the database using MD5 or SHA256 or SHA512 algorithm. For more about session , study the Laravel session documentation click here

You can configure session.php , location: root folder/config/session.php . Such as when user close the browser then destroyed the session.

'expire_on_close' => true 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM