laravel使用"tymon/jwt-auth": "0.5.*"


基於 JWT-Auth 實現 API 驗證

 

基於 JWT-Auth 實現 API 驗證

 

需要提及的幾點:

  • 使用session存在的問題:

    • session和cookie是為了解決http無狀態的方案。session是用戶保存在服務器中的狀態信息,cookie中則保存jsessionId,請求服務器時,服務器讀取jsessionId從而確定用戶的身份信息,而session+cookie用在restful接口上破壞了其“無狀態”的特性,session運行時都是保存在內存中,而隨着認證用戶的增多,服務端的開銷會明顯增大。這也是restful最致力於通過“無狀態”解決的問題。如果使用session,那么restful也就沒有什么意義了

    • session降低了系統擴展性。用戶認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味着用戶下次請求還必須要請求在這台服務器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味着限制了應用的擴展能力

    • cookie不安全,很容易導致跨站請求偽造攻擊(CSRF)

  • token存在的問題:

    • 如,如何確定token的過期時間?如果token的有效期過短,很容易造成用戶用着用着就掉線的情況,降低用戶體驗。但目前看來好像並沒有太好的辦法,只能盡量延長token的有效期,或者每隔一次前端自動向服務端請求一次token

  • 基於 JWT-Auth 的 token 驗證體系

  1. 運行軟件版本

    • laravel 5.7

  2. 安裝 JWT-Auth 擴展包

    composer require tymon/jwt-auth "0.5.*"

或者可以

"require": { "tymon/jwt-auth": "0.5.*"
" }
composer update
 
  1. 安裝完后在配置文件config/app.php 中添加注冊服務提供者和別名:

    ...
    'providers' => [
      ...
       Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
    ]
    ...
    'aliases' => [
      ...
       'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
    ]
  2. 發布資源配置

    php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
  3. 運行以下命令生成密鑰key在生成的config/jwt.php 中

    // 如果運行后報錯,提示ERROR:Method Tymon\JWTAuth\Commands\JWTGenerateCommand::handle() does not exist,將vendor\tymon\jwt-auth\src\Commands\JWTGenerateCommand.php文件中的 fire() 方法修改為 handle()即可正常生成秘鑰
    php artisan jwt:generate
  4. 編輯 app/Http/Kernel.php 添加 jwt.auth 和 jwt.refresh 到應用路由中間件數組:

    protected $routeMiddleware = [
      ...
       'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
    //   'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
    ];
  5. JWTAuth 自身中間件\Tymon\JWTAuth\Middleware\GetUserFromToken 中包含了對生成token的各類情況的驗證,以及異常的拋出。下面是其底層驗證類GetUserFromToken::class

    // file_path : vendor\tymon\jwt-auth\src\Middleware\GetUserFromToken.php
    <?php

    /*
    * This file is part of jwt-auth.
    *
    * (c) Sean Tymon <tymon148@gmail.com>
    *
    * For the full copyright and license information, please view the LICENSE
    * file that was distributed with this source code.
    */

    namespace Tymon\JWTAuth\Middleware;

    use Tymon\JWTAuth\Exceptions\JWTException; //驗證異常類
    use Tymon\JWTAuth\Exceptions\TokenExpiredException;//token過期異常驗證類

    class GetUserFromToken extends BaseMiddleware
    {
       /**
        * Handle an incoming request.
        *
        * @param \Illuminate\Http\Request $request
        * @param \Closure $next
        * @return mixed
        */
       public function handle($request, \Closure $next)
      {
           if (! $token = $this->auth->setRequest($request)->getToken()) {
               return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
          }

           try {
               $user = $this->auth->authenticate($token);
          } catch (TokenExpiredException $e) {
               return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
          } catch (JWTException $e) {
               return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
          }

           if (! $user) {
               return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
          }

           $this->events->fire('tymon.jwt.valid', $user);

           return $next($request);
      }
    }

    其中,調用的respond 的方法在vendor\tymon\jwt-auth\src\Middleware\BaseMiddleware.php文件中

     /**
        * Fire event and return the response.
        *
        * @param string   $event
        * @param string   $error
        * @param int $status
        * @param array   $payload
        * @return mixed
        */
       protected function respond($event, $error, $status, $payload = [])
      {
           $response = $this->events->fire($event, $payload, true);

           return $response ?: $this->response->json(['error' => $error], $status);
      }

    可看到,當出現異常需要返回錯誤信息時,會連帶返回一個fire event 的警告事件對象,這里不做詳述。

  6. 由底層代碼中,可以了解到,我們如果想自定義自己所需要的驗證方法,可以將這GetUserFromToken::class 內容復制到我們自己自定義的中間件中。比如:

    • 創建自定義的驗證中間件App\Http\Middleware\JwtAuth.php

      php artisan make:middleware JwtAuth
    • 全部復制到自定義的中間件中后,校驗下中間件中需要的類是否應用完全,命名空間是否正確等等,檢查無誤后根據需要自行定義需要的驗證功能。

      // demo 
      namespace App\Http\Middleware;

      use Tymon\JWTAuth\Exceptions\JWTException;
      use Tymon\JWTAuth\Exceptions\TokenExpiredException;
      use Closure;
      use Tymon\JWTAuth\Middleware\BaseMiddleware;

      class JwtAuth extends BaseMiddleware
      {
         public function handle($request, \Closure $next)
        {
             if (! $token = $this->auth->setRequest($request)->getToken()) {
                 return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
            }

             try {
                 $user = $this->auth->authenticate($token);
            } catch (TokenExpiredException $e) {
                 return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
            } catch (JWTException $e) {
                 return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
            }

             if (! $user) {
                 return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
            }

             $this->events->fire('tymon.jwt.valid', $user);

             return $next($request);
        }
      }
    • 定義完成后將自定義的中間件放入app\Http\Kernel.php的中間件數組中。

       protected $routeMiddleware = [
      ...
             //'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
             'jwt.auth_self' => \App\Http\Middleware\JwtAuth::class
        ];
    • 添加好后,即可在routes/api.php 中對需要控制的api路由進行驗證控制

      Route::group(['middleware'=>'jwt.auth_self'],function(){
         // 需要控制的api路由
         // ... code
      });
  7. 我們現在可以對請求來的路由進行token的驗證,那么接下來我們就需要生成這個token,讓后續訪問中間件中的請求路由都攜帶這個token就能實現驗證。這里要提一下在安裝JWT-Auth過程中生成的配置文件config/jwt.php

    <?php
       return [
      ...
       /*
       |--------------------------------------------------------------------------
       | User Model namespace
       |--------------------------------------------------------------------------
       |
       | Specify the full namespace to your User model.
       | e.g. 'Acme\Entities\User'
       |
       */
    // 設置你的用戶model,默認為laravel自帶的 User model
       'user' => 'App\User',
    ]

    如果需求需要,可在配置文件中修改用戶mode ,但配置的model 中需要引用Illuminate\Foundation\Auth\User as Authenticatable,並繼承,寫法和User model一致

  8. 具體的請求登錄以及獲取token信息,登出等功能實現,可參考此文章

    Laravel 5 中使用 JWT(Json Web Token) 實現基於API的用戶認證,這里簡單提及下常用到的有關其token的方法

    <?php
       namespace App\Http\Controller;

    use Tymon\JWTAuth\JWTAuth;

    class Auth{
           public function test (JWTAuth $JWTAuth){
               
               // 獲取請求攜帶中的token
               $token = $JWTAuth -> getToken();
               // 獲取token中的用戶信息
               $user = $JWTAuth -> parseToken() -> authenticate();
               // 銷毀此次生成的token
               $JWTAuth->setToken( $JWTAuth->getToken() )->invalidate();
               // 自定義生成token,如果需要
               $JWTAuth->setToken('foo.bar.baz');
          }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM