一、前言
- 如果需要使用 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’ 环境:
1.2. 查看 ‘MariaDB10.3’ 环境:
- 安装 ‘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<?phpreturn [#### 修改为文件缓存'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_KEYCACHE_DRIVER=file #### 修改为文件缓存......others (包括MySQL的配置项) .......JWT_SECRET=Bi43uQQTHxLSnUaIOgTEUT1SkGHiOc1o #### JWT编码时需要的Key
- 完成上述更改之后,快速启动项目,在 ‘PostMan’ 中访问即可看见输出 Lumen 的版本信息。

本文使用以下指令快速启动服务。
# Dir: /jwt-test/php -S localhost:8080 public/index.php
- 下面开始实现 JWT 功能。在下习惯在 ‘app’ 路径想新建一个 ‘Models’ 目录存放模型,因此之后的项目目录结构是:
5.1 修改 ‘bootstrap’ 文件夹下的 ‘app.php’ 如下所示:......others......./app..........others.........../Models <<<<<< 模型文件目录/config <<<<<< 配置文件目录/vendor......others.......
```php <?php
requireonce _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'** 如下所示:<br />```php<?phpreturn ['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’ 如下所示:
<?phpnamespace 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’ 如下所示:
<?phpnamespace 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’,内容如下所示:
<?phpnamespace 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');if (!$token = Auth::refresh(true, true)) {$response['code'] = '5000';$response['errorMsg'] = '系统错误,无法生成令牌';} else {$response['data']['access_token'] = $token;$response['data']['expires_in'] = strval(time() + 86400);}return response()->json($response);}}
5.6. 最后,我们要利用 ‘auth’ 中间件的 ‘api’ 门卫,修改 ‘app/Http/Middleware’ 文件夹下的 ‘Authenticate.php’,内容如下所示:
<?phpnamespace App\Http\Middleware;use Closure;use Illuminate\Contracts\Auth\Factory as Auth;class Authenticate{/*** The authentication guard factory instance.** @var \Illuminate\Contracts\Auth\Factory*/protected $auth;/*** Create a new middleware instance.** @param \Illuminate\Contracts\Auth\Factory $auth* @return void*/public function __construct(Auth $auth){$this->auth = $auth;}/*** 在进入控制器之前,判断并处理请求体** @param \Illuminate\Http\Request $request* @param \Closure $next* @param string|null $guard* @return mixed*/public function handle($request, Closure $next, $guard = null){if ($this->auth->guard($guard)->guest()) {$response['code'] = '4001';$response['errorMsg'] = '无效令牌,需要重新获取';return response()->json($response);}return $next($request);}}
5.7. 创建 ‘user’ 数据表,在数据库中简单填充一条数据。需要注意的是 Lumen 默认数据库使用 ‘utf8mb4’ 编码,如果数据库版本较低,需要修改‘app/Providers/AppServiceProvider.PHP’ 如下:
<?phpnamespace App\Providers;use Illuminate\Support\Facades\Schema;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{/*** Register any application services.** @return void*/public function register(){Schema::defaultStringLength(191);}}

运行测试。在‘routers/router.php’ 中添加相应的路由规则
<?php$router->post('login','AuthController@login');$router->group(['prefix'=>'/','middleware'=>'auth:api'],function () use ($router){$router->post('logout','AuthController@logout');$router->post('refresh','AuthController@refreshToken');});
五、结语
本教程面向新手,更多教程会在日后给出。
- 随着系统升级,软件更新,以后的配置可能有所变化,在下会第一时间测试并且更新教程。
- 欢迎联系在下,讨论建议都可以,之后会发布其它的教程。


