Hỏi về Middleware trong Laravel

Hiện tại mình đang gặp tình huống này, mong mọi người giúp đỡ.

Mình có 2 loại trang, User và Admin.

  • Admin : cần email và password login
  • User: chỉ cần email để login

Mình đang viết 2 loại middleware như sau
IsMember.php dùng để check xem có phải User hay không :

<?php

namespace App\Http\Middleware;
use Auth;
use Closure;

class IsMember
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (Auth::check() ) {
            return $next($request);
        }
        return redirect('/auth');
    }
}

IsAdmin.php check có phải là admin hay không :

<?php

namespace App\Http\Middleware;
use Auth;
use Closure;

class IsAdmin
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        var_dump(Auth::user());
        if (Auth::check()) {
            if (Auth::user()->role == 1) {
                return $next($request);
            }
            return redirect('/');
        }
        return redirect('/login');
    }
}

Ở admin thì mình dùng Authentication của Laravel để login
Còn ở trang user do đặc điểm chỉ dùng email để login vào nên mình viết như sau:

<?php

namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Auth;

class UserController extends Controller
{
    public function showLoginForm()
    {
        return view('user.login');
    }

    public function login(Request $request)
    {
        $validatedData = $request->validate([
            'email' => 'required|email'
        ]);

        $user = User::where('email', trim($request->input('email')))->first();
        if (!$user) {
            return redirect('/auth')->withErrors('This email is not valid');
        } else {
            Auth::login($user);
            return redirect('/');
        }
    }
}

Vấn đề xảy ra: Nếu ai đó biết được email của admin thì sẽ log in được vào trang user, sau đó từ user chuyển sang admin mà không cần nhập password. Ai có cao kiến gì về vấn đề này giúp mình được không? Mình cảm ơn trước.

Có 2 giải pháp

  1. Nếu là account admin thì yêu cầu thêm password
  2. Cứ cho login luôn nhưng có thêm 1 biến session để lưu role, nếu đăng nhập như là member mà mon men qua trang admin thì chuyển qua trang login admin
2 Likes

Chưa thể hiểu được use-case của việc chỉ cần sử dụng email? Marketing chăng?

Bạn có thể bảo mật admin page bằng cách gắn Middleware cho 1 số route nhất định (admin routes):
https://laravel.com/docs/5.7/middleware#assigning-middleware-to-routes

1 Like

mình đang dùng giải pháp này, hình như nó không hiệu quả lắm :frowning:

chưa hiểu ý của bạn cho lắm. như bạn nói ở đây thì giả thuyết đặt ra là dù không bị lộ email của admin thì các user vẫn tự chuyển sang admin được mà không cần nhập pw ?

mình xin góp ý:

(chưa cần bàn tới cách đăng nhập cần pw hay không)
thông thường khi phân quyền thì bạn có cột role chẳng hạn. và bạn xét role = 0 thì là user. role = 1 là admin.
viết điều kiện này trong Middleware và bạn dùng bên Route.

để dùng được Middleware thì bạn cần khai báo đăng kí nó trong file Kernel.php

bạn nên tìm hiểu thêm về cách dùng của Middleware trong Route sẽ thấy rất đơn giản
https://readouble.com/laravel/5.6/en/middleware.html

1 Like

Có lẽ bạn chưa hiểu tình huống của mình, mình có post code của 2 cái middleware, mình đã phân quyền rồi, đã đăng ký middleware cho các route, nhưng trường hợp mình gặp là thế này:
Trang user: chỉ cần email là có thể log in được
Trang admin: cần email và password

Và vấn đề xảy ra như sau:

Nếu 1 user log in dưới dạng email của user, ví dụ như [email protected] là có thể vào được trang user, và dĩ nhiên mình đã set middleware IsAdmin nên không thể nào [email protected] vào được trang admin

Nhưng nếu mình viết middleware như vậy thì sẽ dẫn đến tình trạng sau:
ai đó biết được email của admin (VD như [email protected]) người đó có thể vào trang user bằng email đó, và cũng có thể sang trang admin mà không cần nhập password ( vì khi đăng nhập ở trang user, chỉ cần nhập email [email protected] thì người đó đã log in được, chuyển sang trang admin thì cũng lọt được qua middleware IsAdmin luôn)

Trong IsAdmin Middleware của bạn có dòng này:

if (Auth::user()->role == 1) {

Giá trị của user Admin trong cột role ở table user của bạn có giá trị bằng bao nhiêu? Normal User trong cột role có giá trị bao nhiêu?

Không hiệu quả là không hiệu quả. hình như không hiệu quả lắm tức là bạn đang không biết mình làm cái gì à?

1 Like

role : Admin : 1, Member : 0

Sorry câu chữ mình hơi có vấn đề, giải pháp của bạn là cách mà hiện tại mình đang dùng và nó không hiệu quả. Mình không gắn theo route như bạn, mình gắn theo dạng Controller Middleware:

Tại sao bạn lại cho Authorization (để phân biệt admin/user) Middleware vào construction UserController?

Lại là vấn đề câu chữ rồi, bạn có làm theo giải pháp của mình (mà thực ra là cả ngàn người làm cái đó rồi chứ đâu phải riêng mình) đâu, bạn vẫn đang dùng “cách của bạn đó chứ”

mình xin hỏi thêm, về cách gắn middleware của bạn và mình.

Theo mình hiểu thì việc gắn middleware ở route và middleware ở method constructor trong controller là hoàn toàn giống nhau (https://laracasts.com/discuss/channels/laravel/whats-the-difference-between-a-middleware-used-on-route-level-and-one-in-a-controllers-constructor?page=1)

Việc mình gắn middleware IsMember ở UserController và IsAdmin ở AdminController đâu có gì khác so với việc gắn ở route như cách của bạn?

Bạn gắn Middleware vào constructor thì chỉ có member của class đó ảnh hưởng thôi, các controller khác nếu không inherit từ class đó thì không ảnh hưởng.
Ý mình là mọi route cần authorization đều cần có middleware isAdmin.

Request lifecycle trong trường hợp có middleware là trong mỗi request được khởi tạo, Middleware sẽ check xem property role trong Auth::user() có = 1 hay không, trường hợp chỉ có đăng nhập bằng email thì rõ ràng là ko phù hợp rồi.

Mình không nghĩ có lý do nào để code không hoạt động cả/

Hình như bạn chưa hiểu rõ tình huống của mình

Hiện tại với cách viết Login - Middleware như trên, khi mình login bằng email (không cần pass) ở trang User như : [email protected] (role là Admin) thì đồng nghĩa với việc mình đã login rồi, và khi mình chuyển sang trang Admin, với logic của middleware IsAdmin :

  1. check xem đã đăng nhập chưa? => Rõ ràng là đã login rồi ở trang User bằng email
  2. Check role? => Role = 1

==> lọt qua được Middleware IsAdmin mà không cần password

cách hot-fix nè:

if (user_email == admin@xxx) {
    //bắt nhập pass vô
} else {
    //khỏi cần pass
}

Trường hợp có nhiều email admin, thì viết thêm cái hàm isAdminEmail cho tổng quát nha :slight_smile:

1 Like

Cám ơn mọi người đã đưa giải pháp, hiện tại mình tìm được giải pháp rồi, tuy có hơi chuối và thủ công 1 chút.

Solution: mình không dùng Auth để login vào user page nữa, mình dùng session để lưu email và check nếu không có session thì đá ra trang đăng nhập, và cách này cũng giải quyết luôn là nếu ai đó dùng email [email protected] vào thì chỉ vào được user page, lúc vào admin page thì phải đăng nhập bằng email và pass :smiley:

Middleware IsMember sau khi check bằng session:

<?php

namespace App\Http\Middleware;
use Auth;
use Closure;

class IsMember
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (!$request->session()->has('logged')) {
            return redirect('/auth');
        }

        return $next($request);
    }
}

method Login bằng Session ở UserController:

<?php

namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use App\Book;
use Auth;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('member')->except(['showLoginForm', 'login',]);
    }

    public function showLoginForm()
    {
        return view('user.login');
    }

    public function login(Request $request)
    {
        $validatedData = $request->validate([
            'email' => 'required|email'
        ]);
        
        $email = trim($request->input('email'));
        $user = User::where('email', $email)->first();
        if (!$user) {
            return redirect('/auth')->withErrors('This email is not valid');
        } else {
            session(['logged' => $email]);
            return redirect('/');
        }
    }

    public function index(Request $request)
    {
        $bookList = new Book;
        $listStatus = $bookList->listStatus;
        $bookList   = $bookList::filter($request)->orderBy('title','asc');
        $bookList   = $bookList->paginate(15);
        return view('user.index', ['bookList' => $bookList, 'listStatus' => $listStatus]);
    }
}
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?