在Laravel Apps 內使用兩種Auth進行登入驗證

在Laravel Apps 內使用兩種Auth進行登入驗證

前言

一般我們在建立一個Apps 時也許會採用Laravel內已經預設好的驗證方式authentication,且已經幫你包的好好的只要下一個指令

php artisan make:auth

一切都幫你處理的妥妥盪盪連前端頁面都幫你生出來真是好棒棒!而且他也提供了很方便的幾個功能,如 middleware, event handler等。

但是當你的Apps 裡面需要第二個User呢?

舉例來說,很多公司在開發時都會有個需求:我想要有個管理者後台跟廠商後台,所以我就需要有兩個table 去處理登入頁面的事情,如果我想要用User當做管理者後台的登入資料。這個時候就需要有另外一個東西來處理廠商後台的登入內容。

規劃

如果以廠商後台來處理的話

  1. 我們應該會有個vendor的table,而裡面需要包含登入帳號及密碼的欄位(當然如果你要使用remeber_token的話也可以加入)。
    1. 因為會有兩個登入頁,所以勢必會使用到一個登入的路由,並定義名稱。
    2. 由於有些廠商可能沒有Email(我們有些專案確實有考量到這點),所以我們先暫時不處理email及註冊的問題,統一由管理者統一開立帳號。

實作

1. 創建table

php artisan make:model Vendor

接著修改table 欄位

class CreateVendorsTable extends Migration {
public function up()
{
    //你要的欄位...
    $table->string('account')->comment('廠商登入帳號');
    $table->string('password')->comment('廠商登入密碼');
    $table->string('remember_token')->nullable()->comment('token');
    //你要的欄位...
}
...
}

接著你要讓你的Model Vendor繼承Illuminate\Foundation\Auth\User

//...
'vendor' => [
    'driver' => 'session',
    'provider' => 'vendors',
    ]
],
  • providerprovider要定義對應的table及driver的方式。driver可選擇databaseeloquent,我們這邊選擇eloquent,table則選擇vendor
    'providers' => [
      'users' => [
          'driver' => 'eloquent',
          'model' => App\Models\User::class,
      ],
    
      'vendors' => [
          'driver' => 'eloquent',
          'model' => App\Models\Vendor::class,
      ],
    ],

3. 建立LoginController

我們這邊可以先研究app/Http/Controllers/Auth/LoginController.php,並把需要的項目拉出來加以修改。

<?php

namespace App\Http\Controllers\Vendor;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    /**
     * 登入後導向的畫面
     * @var string
     */
    protected $redirectTo = '/index';

    /** 
     * 透過guest middleware,若登入後再回到login時會幫你自動跳轉到指定頁面(需在RedirectIfAuthenticated.php 內設定跳轉路徑)。guest後面可以帶入guard的參數,這邊使用vendor
     */

    public function __construct()
    {
        $this->middleware('guest:vendor', ['except' => 'logout']);
    }

    /**
     * 登入頁面
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('vendor/login');
    }

    /**
     * 定義登入時須輸入的table對應欄位,這邊我們在table內輸入的是account。
     * @return string
     */
    public function username()
    {
        return 'account';
    }

    /**
     * 選擇要使用的guard
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard()
    {
        return \Auth::guard('vendor');
    }

    /**
     * 登出功能
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $this->guard()->logout();

        $request->session()->invalidate();

        return redirect(route('vendor::loginForm'));
    }
}

4. 修改路由及中間層

因為會有兩個登入頁面,所以需先設定兩個不同的domain。筆者習慣先在.env檔及config/app.php檔內定義domain。

這邊會分為本地環境及正式環境的域名設定,只是筆者的開發習慣,不一定需要實作

//.env
DOMAIN=admin.mywebsite.tw
DOMAIN_VENDOR=vendor.mywebsite.tw

//app.php
<?php
'domain' => env('DOMAIN', 'admin.myproductiondomain.tw'),
'domain_vendor' => env('DOMAIN_VENDOR', 'vendor.myproductiondomain.tw'),

接著就可以在routes/web.php內定義

//web.php
<?php
Route::group(['domain' => config('app.domain')], function () {
//...以user為auth的登入
//Auth::routes();
}
Route::group(['domain' => config('app.domain_vendor')], function () {
    Route::group(['namespace' => 'Vendor'], function () {
    /**
    * 登入
    */
    Route::get('/login', 'LoginController@showLoginForm')->name('loginForm');//登入頁面
    Route::post('/login', 'LoginController@login')->name('login');//登入功能,我們完全繼承AuthenticatesUsers的登入流程,若需要可以修改。
    Route::group(['middleware' => 'auth:vendor'], function () {//建立登入的middleware,可以直接用auth:{guard}
        Route::get('/logout', 'LoginController@logout')->name('logout');
        ...
});

中間層則要找到app\Http\Middleware\RedirectIfAuthenticated.php處理

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

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)
{
if (Auth::guard($guard)->check()) {
return redirect('/index'); //改成『若登入後再回到登入頁面時你要跳轉』的頁面,這邊應該會在LoginController的屬性$redirectTo一樣。
}

return $next($request);
}
}

5. 處理unauthenticated

當我們在middleware內觸發auth fail時,需要讓網址導向某個路徑,筆者這邊建議是直接回到登入頁面即可。

開啟app/Exceptions/Handler.php

<?php
...
protected function unauthenticated($request, AuthenticationException $exception)
{
//...

    if (strstr($request->url(), config('app.domain_vendor'))) {
        return redirect()->guest(route('vendor::loginForm'));
    }
//...
}

到這邊就基本上完成基礎的 Auth 登入設定。

6. 開心的使用Auth Facedes囉!

這邊就可以隨意的使用Auth Facade帶給我們的方便囉!以下顯示幾個筆者常用的技巧,其實官網都有詳細記載囉~

<?php
//取得登入者帳號
$user = Auth::user();

//取的登入者id
$id = Auth::id();

//登出
Auth::logout();

//確認是否登入
if (Auth::check()) {
// The user is logged in...
}

//但是筆者發現當你使用兩個Auth時使用check時會發現不知道是哪一個guard,如果你想要判別是屬於哪一個Auth時可以用
if (Auth::getProvider()->getModel() == 'App\Models\Vendor') {
//...

筆者常用的基本上就這樣而已,如果你有更多的需求筆者建議你可以可以上官網查詢,或是直接看Laravel內的檔案,如Illuminate\Auth\SessionGuardIlluminate\Auth\EloquentUserProvider裡面都有詳細記載Auth Facade的方法。

後記

當初筆者是直接看原始碼+官網去推敲整個的過程,後來再去讀一次官網的內容才發現其實都有寫到而且還更加詳細XD,不過第一次看的時候真的是以為自己在看天書阿~