如何在一個 Laravel 應用中使用多個認證看守器(Guards)

文章轉發自專業的Laravel開發者社區,原始連接:learnku.com/laravel/t/2…php

若是你使用 Laravel 有一段時間了,你應該聽過不少關於多種身份認證的信息。你也應該聽過不少認證看守器。可是若是你對 Laravel 還不熟悉,多種身份認證可讓不一樣類別的用戶訪問相同應用的不一樣/相似的部分。css

你可能但願在你的 Laravel 應用程序中使用多種身份認證的緣由有不少。例如,你有一個運行整個公司的大型應用程序。客戶還經過相同的應用程序與公司的產品和服務進行交互。該應用程序應該還有一個博客,公司中還有一個部門專門管理這個博客。html

從上面的程序能夠看出,這裏面已經有三組用戶。客戶,咱們可讓他們使用一個特定的身份驗證過程來訪問應用;做者,他們應該有另外一個身份驗證過程,甚至有權限來啓用一個更健壯的內容管理流程;公司的其餘成員,您應該根據他們不一樣的角色來給他們展現不一樣的功能菜單。 如今,讓咱們看看如何爲不一樣類別的用戶建立多個身份驗證。mysql

要求

  1. 知識儲備 PHP (版本 >= 7.1.3)。
  2. 知識儲備 Laravel (版本 5.6.x)。
  3. 電腦已安裝Composer  (版本 >= 1.3.2)。
  4. 電腦已安裝 Laravel 框架 。

開始

若是你已經知足上面清單上的全部條件,那麼你能夠繼續本教程, 咱們將建立擁有三個用戶類的應用程序 — adminwriteruser。咱們將爲這三個用戶類創建不一樣的 guards限制。laravel

建立應用

咱們建立一個新的Laravel應用程序,在終端執行下面的命令能夠建立一個新的Laravel應用程序。git

$ laravel new multi-auth
    $ cd multi-auth

複製代碼

建立數據庫

咱們在應用程序中使用SQLite數據庫。 它是一個輕量級而且很是快的文件類型數據庫。 咱們能夠在命令行中建立數據庫:github

$ touch database/database.sqlite

複製代碼

在你的應用中,打開 .env 文件並把下面的配置:web

DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret

複製代碼

修改成:sql

DB_CONNECTION=/absolute/path/to/database.sqlite

複製代碼

這將會確保讓咱們的應用使用SQLite數據庫驅動程序。數據庫

建立數據庫遷移

咱們將會爲adminswriters 表建立數據表遷移文件,相似users表的遷移文件。這都是簡單的用戶表,未來你能夠根據你的需求來擴展他們。

建立admins數據表遷移文件

運行下面的命令建立admins數據表遷移文件:

$ php artisan make:migration create_admins_table

複製代碼

在 database/migrations 目錄, 打開並修改admins數據表遷移文件:

// database/migrations/<timestamp>_create_admins_table.php

    [...]
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('is_super')->default(false);
            $table->rememberToken();
            $table->timestamps();
        });
    }
    [...]
複製代碼

咱們建立了一個簡單的數據表遷移文件,並定義了咱們要使用的數據表的字段。Eloquent爲咱們提供了方法來定義數據庫表字段的數據類型。咱們使用它們定義表字段的數據類型。

記住,你能夠按你的想法配置你的表。

Create migration for writers

經過如下命令來建立 writers 的數據遷移表:

$ php artisan make:migration create_writers_table

複製代碼

打開剛纔建立的遷移表文件進行修改:

database/migrations/<timestamp>_create_writers_table.php
    [...]
    public function up()
    {
        Schema::create('writers', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->boolean('is_editor')->default(false);
            $table->rememberToken();
            $table->timestamps();
        });
    }
    [...]
複製代碼

咱們僅僅建立了一個簡單的遷移文件並定義了幾個須要的字段。Eloquent 提供了不少定義數據庫表字段類型的方法,因此操做起來比較容易。

遷移數據庫

如今咱們已經定義了表,咱們開始遷移數據庫:

$ php artisan migrate

複製代碼

設置模型

咱們的應用程序有不一樣類別的用戶,他們存儲在不一樣的數據庫表中,要爲使用這些不一樣的表的用戶進行身份驗證,咱們必須爲它們定義模型。這些模型相似於用戶模型並擴展可驗證類。

Admin 模型

執行下面的命令,建立 Admin 模型:

$ php artisan make:model Admin

複製代碼

打開 Admin 模型文件 app/Admin.php,添加以下代碼:

// app/Admin.php
    <?php

    namespace App;

    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    class Admin extends Authenticatable
    {
        use Notifiable;

        protected $guard = 'admin';

        protected $fillable = [
            'name', 'email', 'password',
        ];

        protected $hidden = [
            'password', 'remember_token',
        ];
    }
複製代碼

當你要對一個模型的訪問作用戶認證,但又不想用默認的 user 看守器時,就須要指定看守器。在這個例子中,這個看守器就是 admin

fillable 數組中,指定了用戶能夠操做的數據庫字段。也就是明確地告知 Laravel:

當我執行 createupdate 方法時,我會把要操做的字段以數組的形式傳給你,可是你只能夠往數據庫中插入 fillable 數組中指定的字段。

這樣,就能夠防止用戶任意操做咱們不但願被更改的字段。

hidden 數組中,能夠指定不但願被返回的字段。

Writers 模型

開始爲 Writer 建立模型,咱們運行下面的命令:

$ php artisan make:model Writer

複製代碼

接着咱們打開 Writer 模型,用下面的代碼替換掉:

// app/Writer.php
    <?php

    namespace App;

    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;

    class Writer extends Authenticatable
    {
        use Notifiable;

        protected $guard = 'writer';

        protected $fillable = [
            'name', 'email', 'password',
        ];

        protected $hidden = [
            'password', 'remember_token',
        ];
    }
複製代碼

定義守衛(Guards)

Laravel 的守衛定義瞭如何爲每一個請求進行身份驗證。默認有一些用於身份驗證的守衛,但也能夠自定義。 這樣可使用 Laravel 默認的認證系統和自定義的 AdminWriter 模型。

打開 config/auth.php 並添加新的守衛,以下:

// config/auth.php

    <?php

    [...]
    'guards' => [
        [...]
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
        'writer' => [
            'driver' => 'session',
            'provider' => 'writers',
        ],
    ],
    [...]
複製代碼

添加了兩個新的守衛 adminwriter,並設置了提供者。經過提供者來進行身份驗證。

如今,完善 providers 數組:

// config/auth.php

    [...]
    'providers' => [
        [...]
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],
        'writers' => [
            'driver' => 'eloquent',
            'model' => App\Writer::class,
        ],
    ],
    [...]
複製代碼

根據上一步定義的看守器,咱們具體完善了 providers 數組的信息。用 eloquent 做爲驅動,是由於咱們要用 Eloquent ORM 與數據庫交互。

假設咱們要用別的 ORM 好比 RedBeanPHP 來操做數據庫,那就要把驅動設置爲 redbeanphp,而不是 eloquent。 至於 model ,就是具體要進行訪問驗證的模型類。

設置控制器

爲了使這些看守器發揮各自的做用,咱們有兩種方案能夠選擇,一是修改現有的驗證控制器,二是建立新的控制器。你能夠根據具體須要進行選擇。在這個例子中,咱們選擇前者。

修改 LoginController

打開 LoginController,並作以下編輯:

// app/Http/Controllers/Auth/LoginController.php

    <?php

    namespace App\Http\Controllers\Auth;

    use App\Http\Controllers\Controller;
    use Illuminate\Foundation\Auth\AuthenticatesUsers;
    [...]
    use Illuminate\Http\Request;
    use Auth;
    [...]
    class LoginController extends Controller
    {
        [...]
        public function __construct()
        {
            $this->middleware('guest')->except('logout');
            $this->middleware('guest:admin')->except('logout');
            $this->middleware('guest:writer')->except('logout');
        }
        [...]
    }
複製代碼

經過中間件的方式限制訪問控制器裏的方法。咱們細分了訪客的類型,這樣,以一個類型的用戶登陸以後,若是再想切換身份,以另外一種類型的用戶登陸時,就會被引導到預約義的身份驗證頁面。

舉個例子: 若是我在電腦上以管理員的身份登陸了,個人做家同事就沒法以做者的身份登陸他的帳戶。

這個驗證操做是很重要的,這樣就不會由於 session 信息的混亂,而給應用數據帶來潛在的風險。

如今定義 admins 登錄:

// app/Http/Controllers/Auth/LoginController.php

    [...]
    public function showAdminLoginForm()
    {
        return view('auth.login', ['url' => 'admin']);
    }

    public function adminLogin(Request $request)
    {
        $this->validate($request, [
            'email'   => 'required|email',
            'password' => 'required|min:6'
        ]);

        if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {

            return redirect()->intended('/admin');
        }
        return back()->withInput($request->only('email', 'remember'));
    }
    [...]
複製代碼

咱們已經設置了一個方法來返回管理員的登陸頁面。 咱們將對全部用戶類型使用相同的頁面,並僅更改它們發送到的 URL 。 爲咱們節省了許多咱們能夠避免編寫的代碼。

咱們還定義了 adminLogin 方法,該方法檢查是否提供了正確的憑據。 而後咱們嘗試使用 admin guard 登陸用戶。 在嘗試登陸時設置此守衛很是重要,以便Auth Facade將檢查正確的表匹配憑據。 它還將設置咱們的身份驗證,以便咱們能夠根據登陸用戶的類型限制頁面。 咱們將通過身份驗證的用戶重定向到特定 URL,並將未經身份驗證的用戶返回登陸頁面。

如今,讓咱們爲 writers 也作一樣的事情:

// app/Http/Controllers/Auth/LoginController.php

    [...]
    public function showWriterLoginForm()
    {
        return view('auth.login', ['url' => 'writer']);
    }

    public function writerLogin(Request $request)
    {
        $this->validate($request, [
            'email'   => 'required|email',
            'password' => 'required|min:6'
        ]);

        if (Auth::guard('writer')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {

            return redirect()->intended('/writer');
        }
        return back()->withInput($request->only('email', 'remember'));
    }
    [...]
複製代碼

咱們的登陸設置好了。萬歲!!!

修改 RegisterController

打開 RegisterController 並進行以下編輯:

// app/Http/Controllers/Auth/RegisterController.php

    <?php
    [...]
    namespace App\Http\Controllers\Auth;
    use App\User;
    use App\Admin;
    use App\Writer;
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Hash;
    use Illuminate\Support\Facades\Validator;
    use Illuminate\Foundation\Auth\RegistersUsers;
    use Illuminate\Http\Request;
    [...]
    class RegisterController extends Controller
    {
        [...]
        public function __construct()
        {
            $this->middleware('guest');
            $this->middleware('guest:admin');
            $this->middleware('guest:writer');
        }
      [...]
    }
複製代碼

咱們已經設置了中間件,控制器未來會使用的到,就像咱們使用 LoginController同樣。

如今,讓咱們設置爲不一樣用戶返回不一樣的註冊頁面的方法:

// app/Http/Controllers/Auth/RegisterController.php

    [...]
    public function showAdminRegisterForm()
    {
        return view('auth.register', ['url' => 'admin']);
    }

    public function showWriterRegisterForm()
    {
        return view('auth.register', ['url' => 'writer']);
    }
    [...]
複製代碼

這跟咱們在顯示不一樣登陸頁面時所作的相似。

如今,咱們能夠定義一個建立 admin 的方法:

// app/Http/Controllers/Auth/RegisterController.php

    [...] 
    protected function createAdmin(Request $request)
    {
        $this->validator($request->all())->validate();
        $admin = Admin::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password']),
        ]);
        return redirect()->intended('login/admin');
    }
    [...] 
複製代碼

接下來,讓咱們定義建立 writer 的方法:

// app/Http/Controllers/Auth/RegisterController.php

    [...] 
    protected function createWriter(Request $request)
    {
        $this->validator($request->all())->validate();
        $writer = Writer::create([
            'name' => $request['name'],
            'email' => $request['email'],
            'password' => Hash::make($request['password']),
        ]);
        return redirect()->intended('login/writer');
    }
    [...] 
複製代碼

註冊完成。

設置驗證頁面

咱們將用 Laravel 自帶的腳手架爲驗證系統生成頁面和控制器。執行以下命令:

$ php artisan make:auth

複製代碼

這樣就能夠生成模版文件 resources/views/auth, 同時還會生成相應的路由,從而完成基本的驗證操做。很酷對不對?

打開 login.blade.php 文件並作以下編輯:

// resources/views/auth/login.blade.php
    [...]
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div>

                    <div class="card-body">
                        @isset($url)
                        <form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}">
                        @else
                        <form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
                        @endisset
                            @csrf
        [...]
    </div>
複製代碼

這裏咱們會判斷是否有 url 參數。若是有,就把這個參數帶到表單提交的路由裏。同時也會修改表單頁的頭部信息,從而實現根據不一樣的登陸參數展現用戶的類型。

打開 register.blade.php 模板文件並編輯,以下:

// resources/views/auth/register.blade.php

    [...]
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div>

                    <div class="card-body">
                        @isset($url)
                        <form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('Register') }}">
                        @else
                        <form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
                        @endisset
                            @csrf
        [...]
    </div>
複製代碼

咱們複製了登陸頁面的操做。

建立通過身份驗證的用戶將訪問的頁面

如今,咱們已經完成了登陸和註冊頁面的設置,讓咱們讓 admin 和 writers 在驗證頁面時看到這些頁面。打開終端並運行如下命令來建立新文件。接下來,咱們將把相應的代碼片斷插入到文件中。

$ touch resources/views/layouts/auth.blade.php
    $ touch resources/views/admin.blade.php
    $ touch resources/views/writer.blade.php
    $ touch resources/views/home.blade.php

複製代碼

將此代碼塊插入 auth.blade.php 文件:

// resources/views/layouts/auth.blade.php

    <!DOCTYPE html>
    <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>

        <!-- Fonts -->
        <link rel="dns-prefetch" href="https://fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">

        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div id="app">
            <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
                <div class="container">
                    <a class="navbar-brand" href="{{ url('/') }}">
                        {{ config('app.name', 'Laravel') }}
                    </a>
                    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                        <span class="navbar-toggler-icon"></span>
                    </button>

                    <div class="collapse navbar-collapse" id="navbarSupportedContent">
                        <!-- Left Side Of Navbar -->
                        <ul class="navbar-nav mr-auto">

                        </ul>

                        <!-- Right Side Of Navbar -->
                        <ul class="navbar-nav ml-auto">
                            <!-- Authentication Links -->
                           <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    Hi There <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>

            <main class="py-4">
                @yield('content')
            </main>
        </div>
    </body>
    </html>
複製代碼

接下來, 將此代碼塊插入 admin.blade.php 文件:

// resources/views/admin.blade.php

    @extends('layouts.auth')

    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                        Hi boss!
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection
複製代碼

打開  writer.blade.php 模板文件並編輯,以下:

// resources/views/writer.blade.php

    @extends('layouts.auth')

    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                        Hi there, awesome writer
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection
複製代碼

最後, 打開  home.blade.php  模板文件並替換爲:

// resources/views/home.blade.php

    @extends('layouts.auth')

    @section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Dashboard</div>

                    <div class="card-body">
                         Hi there, regular user
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endsection
複製代碼

設置路由

咱們的應用程序準備差很少了。讓咱們定義到目前爲止建立的全部方法的路由。打開 routes/web.php 並替換爲:

// routes/web.php

    <?php
    Route::view('/', 'welcome');
    Auth::routes();

    Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm');
    Route::get('/login/writer', 'Auth\LoginController@showWriterLoginForm');
    Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm');
    Route::get('/register/writer', 'Auth\RegisterController@showWriterRegisterForm');

    Route::post('/login/admin', 'Auth\LoginController@adminLogin');
    Route::post('/login/writer', 'Auth\LoginController@writerLogin');
    Route::post('/register/admin', 'Auth\RegisterController@createAdmin');
    Route::post('/register/writer', 'Auth\RegisterController@createWriter');

    Route::view('/home', 'home')->middleware('auth');
    Route::view('/admin', 'admin');
    Route::view('/writer', 'writer');
複製代碼

修改咱們的用戶在通過身份驗證後的重定向

當用戶通過身份驗證時,修改用戶的重定向方式很是重要。默認狀況下,Laravel將全部通過身份驗證的用戶重定向到  /home。若是不修改重定向,將會獲得下面的錯誤。

因此, 爲了解決這個, 打開 app/Http/Controllers/Middleware/RedirectIfAuthenticated.php 文件並替換爲:

// app/Http/Controllers/Middleware/RedirectIfAuthenticated.php

    <?php

    namespace App\Http\Middleware;

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

    class RedirectIfAuthenticated
    {
        public function handle($request, Closure $next, $guard = null)
        {
            if ($guard == "admin" && Auth::guard($guard)->check()) {
                return redirect('/admin');
            }
            if ($guard == "writer" && Auth::guard($guard)->check()) {
                return redirect('/writer');
            }
            if (Auth::guard($guard)->check()) {
                return redirect('/home');
            }

            return $next($request);
        }
    }
複製代碼

RedirectIfAuthenticated 中間件接收 auth 守衛做爲參數。當咱們試圖訪問任何針對通過身份驗證的用戶的頁面時,將觸發此中間件。而後,咱們能夠肯定用戶擁有的身份驗證類型並相應地重定向它們。

修改身份驗證異常處理程序

當用戶被重定向時會發生一件煩人的事情。若是用戶試圖訪問 /writer,但沒有通過身份驗證,那麼用戶會被重定向到 /login/writer,他們被重定向到 /login 這不是咱們想要的。

爲了確保當用戶嘗試訪問 /writer 時,它們被重定向到 /login/writer 或者一樣用於 /admin,咱們必須修改異常處理程序。 在 app/Exceptions 中打開處理程序文件並添加如下內容:

// app/Exceptions/Handler.php

    <?php

    namespace App\Exceptions;

    use Exception;
    use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
    [...]
    use Illuminate\Auth\AuthenticationException;
    use Auth; 
    [...]
    class Handler extends ExceptionHandler
    {
       [...] 
        protected function unauthenticated($request, AuthenticationException $exception)
        {
            if ($request->expectsJson()) {
                return response()->json(['error' => 'Unauthenticated.'], 401);
            }
            if ($request->is('admin') || $request->is('admin/*')) {
                return redirect()->guest('/login/admin');
            }
            if ($request->is('writer') || $request->is('writer/*')) {
                return redirect()->guest('/login/writer');
            }
            return redirect()->guest(route('login'));
        }
    }
複製代碼

咱們剛剛添加的 unauthenticated 方法解決了咱們遇到的這個問題。 它默認接收一個 AuthenticationExpection 異常,它攜帶該保護信息。 遺憾的是,咱們沒法訪問它,由於它受到保護(但願Laravel 5.7可以提供訪問它的方法)。

咱們的解決方法是使用 request→is()。這會檢查咱們嘗試訪問的URL。若是咱們沒有絕對URL或者咱們有路由組,它也能夠檢查URL模式。

在咱們的例子中,咱們首先檢查是否收到了 JSON 請求並單獨處理異常。 而後咱們檢查咱們是否正在嘗試訪問 /admin 或任何以 admin 開頭的 URL。 咱們將用戶重定向到相應的登陸頁面。 咱們也檢查 writer

這對咱們來講是一個很好的解決方法,但這意味着咱們必須知道咱們想要訪問的絕對URL,或者至少對咱們的守衛保護的全部路由都有相同的前綴。

運行應用程序

如今咱們的應用程序已經準備好了,運行下面的命令來啓動它:

$ php artisan serve

複製代碼

它一般應該是可用的 http://localhost:8000.

先經過訪問 http://localhost:8000/register/writer 和 http://localhost:8000/register/admin 這兩個連接來分別註冊做者和管理員. 而後訪問 http://localhost:8000/login/writer 和 http://localhost:8000/login/admin 這兩個連接分別登陸做者和管理員。

結語

在本教程中,咱們深刻研究了 Laravel 身份驗證。 咱們定義了多個守衛來處理多個身份驗證和訪問控制。咱們還爲通過身份驗證的用戶處理重定向,爲未經身份驗證的用戶處理重定向。

若是您徹底遵循本指南,您將可以爲具備不一樣用戶類型的應用程序(多是[多租戶](en.wikipedia.org/wiki/Multit… 儘管如此,嘗試擴展你所看到的並分享你想出的東西。

本文中應用程序的源代碼可在 GitHub

相關文章
相關標籤/搜索