一、前言
- 如果需要使用 Passport,可以參考在下之前的教程: 'Lumen5.4配置OAuth2.0【強迫症,就是要用最新版本的Lumen】' 。
- 由於原作者 文檔 的簡潔性,同時 Lumen 下的 JWT 與 Laravel 略有不同,導致新手初學不易理解。
- 在下經過多番考究,總結出 Lumen 使用 JWT 的基本過程。同時給出 JWT的介紹 。
- 經過少數同學的反饋,教程太過倉促,現重新補充完整。
二、說明
- 不知不覺 Lumen 已經更新到 '5.6.x' 版本,因此本文也緊跟腳步,使用最新版的 Lumen 進行講解,最重要的是 Laravel/Lumen 5.6.x 版本只支持 'PHP7.1' 及以上。
- 本文使用 'tymon/jwt-auth: ^1.0.0-rc.2' 版本 (不推薦使用該擴展包的 0.5 版本) 的擴展包,搭配 Laravel 開箱即用的 'Auth' 組件實現 JWT 認證。
- 操作環境:'Windows 7' + 'PHP7.2' + 'MariaDB10.3'。上述環境在下均已測試多次,現分享出本人至今 'Windows' 下正常開發使用的 整合壓縮包 。
三、准備部分
- 檢查 'Windows' 上的開發環境是否正常。
1.1. 查看 'PHP7.2' 環境:
PHP
1.2. 查看 'MariaDB10.3' 環境:
MariaDB - 安裝 'PostMan' 以及 'Navicat Premium' ,其他類似軟件產品亦可,根據個人喜好就行。
- 操作之前查看 'JWT的介紹' ,對理解后文大有裨益。
四、實現部分
- 使用 'Composer' 安裝最新的 Lumen 到本地。
composer create-project laravel/lumen jwt-test --prefer-dist
- 進入項目 'jwt-test' 中,安裝 'tymon/jwt-auth: ^1.0.0-rc.2' 到本地。
composer require tymon/jwt-auth ^1.0.0-rc.2
- 剛剛初始化的 Lumen 項目可能會有一個小小的 BUG,因為 Lumen 默認會加載 Memcached 作為緩存,而部分開發者並沒有使用 Memcached,所以需要在使用 Lumen項目之前,修改緩存配置。
3.1.首先模擬 Laravel 目錄結構,復制'vender/laravel/lumen-framework'下的 'config 目錄到 'jwt-test' 根路徑。復制完成以后 'jwt-test' 的根目錄結構如下:
/app
......others.......
/config <<<<<< 配置文件目錄
/vendor
......others.......
3.2. 以后的配置文件,都只需要在根路徑下的 'config目錄操作即可,所以接着修改目錄中的 cache.php 文件:
# Dir: /jwt-test/config/cache.php
<?php return [ #### 修改為文件緩存 'default' => env('CACHE_DRIVER', 'file'), #### 同時刪除了下面的Memcached配置 'stores' => [ 'apc' => [ 'driver' => 'apc', ], 'array' => [ 'driver' => 'array', ], 'database' => [ 'driver' => 'database', 'table' => env('CACHE_DATABASE_TABLE', 'cache'), 'connection' => env('CACHE_DATABASE_CONNECTION', 'mysql_a'), ], 'file' => [ 'driver' => 'file', 'path' => storage_path('framework/cache'), ], 'redis' => [ 'driver' => 'redis', 'connection' => env('CACHE_REDIS_CONNECTION', 'cache'), ] ], 'prefix' => env('CACHE_PREFIX', 'wz'), ];
同時修改根路徑下的 '.env 文件:
# Dir: /jwt-test/.env ......others....... APP_KEY=9TBF8FrZZgYBoM0AzKjkii/yb6TJVm11 #### Lumen默認沒有設置APP_KEY CACHE_DRIVER=file #### 修改為文件緩存 ......others (包括MySQL的配置項) ....... JWT_SECRET=Bi43uQQTHxLSnUaIOgTEUT1SkGHiOc1o #### JWT編碼時需要的Key
- 完成上述更改之后,快速啟動項目,在 'PostMan' 中訪問即可看見輸出 Lumen 的版本信息。
On Server
本文使用以下指令快速啟動服務。
# Dir: /jwt-test/ php -S localhost:8080 public/index.php
- 下面開始實現 JWT 功能。在下習慣在 'app' 路徑想新建一個 'Models' 目錄存放模型,因此之后的項目目錄結構是:
......others.......
/app
..........others.......
..../Models <<<<<< 模型文件目錄
/config <<<<<< 配置文件目錄
/vendor
......others.......
5.1 修改 'bootstrap' 文件夾下的 'app.php' 如下所示:
<?php require_once __DIR__.'/../vendor/autoload.php'; try { (new Dotenv\Dotenv(__DIR__.'/../'))->load(); } catch (Dotenv\Exception\InvalidPathException $e) { // } $app = new Laravel\Lumen\Application( realpath(__DIR__.'/../') ); // 取消注釋 $app->withFacades(); $app->withEloquent(); $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class ); $app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class ); // 取消注釋 $app->routeMiddleware([ 'auth' => App\Http\Middleware\Authenticate::class, ]); // 取消注釋 $app->register(App\Providers\AppServiceProvider::class); $app->register(App\Providers\AuthServiceProvider::class); $app->register(App\Providers\EventServiceProvider::class); // 新增JWT的注冊 $app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class); $app->router->group([ 'namespace' => 'App\Http\Controllers', ], function ($router) { require __DIR__.'/../routes/web.php'; }); return $app;
5.2. 修改 'config' 文件夾下的 'auth.php' 如下所示:
<?php return [ 'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], 'guards' => [ 'api' => [ 'driver' => 'jwt', #### 更改為JWT驅動 'provider' => 'users', ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => \App\Models\User::class, #### 指定用於token驗證的模型類 ], ], 'passwords' => [ #### Lumen默認無session,所以該字段無意義 // ], ];
5.3. 修改 'app/Providers' 文件夾下的 'AuthServiceProvider.php' 如下所示:
<?php namespace App\Providers; use App\Models\User; use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Boot the authentication services for the application. * * @return void */ public function boot() { // 當使用auth中間件的api門衛的時候驗證請求體 $this->app['auth']->viaRequest('api', function ($request) { return app('auth')->setRequest($request)->user(); }); } }
5.4. 修改 'app/Models' 文件夾下的 'User.php' 如下所示:
<?php namespace App\Models; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Laravel\Lumen\Auth\Authorizable; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject { use Authenticatable, Authorizable; protected $table = 'users'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'username', 'email', ]; /** * The attributes excluded from the model's JSON form. * * @var array */ protected $hidden = [ 'password', ]; /** * JWT * * @author AdamTyn */ public function getJWTIdentifier() { return $this->getKey(); } /** * JWT * * @author AdamTyn */ public function getJWTCustomClaims() { return []; } }
5.5. 在 'app/Http/Controller' 文件夾下新建 'UserController.php',內容如下所示:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; class AuthController extends Controller { /** * 登錄 * * @author AdamTyn * * @param \Illuminate\Http\Request; * @return \Illuminate\Http\Response; */ public function login(Request $request) { $response = array('code' => '0'); try { $user = \App\Models\User::where('username', $request->input('username')) ->where('password', $request->input('password'))->first(); if (!$token = Auth::login($user)) { $response['code'] = '5000'; $response['errorMsg'] = '系統錯誤,無法生成令牌'; } else { $response['data']['user_id'] = strval($user->id); $response['data']['access_token'] = $token; $response['data']['expires_in'] = strval(time() + 86400); } } catch (QueryException $queryException) { $response['code'] = '5002'; $response['msg'] = '無法響應請求,服務端異常'; } return response()->json($response); } /** * 用戶登出 * * @author AdamTyn * * @return \Illuminate\Http\Response; */ public function logout() { $response = array('code' => '0'); Auth::invalidate(true); return response()->json($response); } /** * 更新用戶Token * * @author AdamTyn * * @param \Illuminate\Http\Request; * @return \Illuminate\Http\Response; */ public function refreshToken() { $response = array('code' => '0')