Laravel 5.5 使用 Jwt-Auth 實現 API 用戶認證、刷新令牌


目錄

jwt概述:

1、安裝jwt

2.注冊服務提供者和別名

3、發布配置文件

4.在發布的配置中生成key:

5.配置 api的驗證模式

6、修改Model 

7、 配置項詳解

8、自定義RefreshToken 中間件

9、注冊RefreshToken 中間件

10、更新異常處理的handler

11、添加新的控制器

12、添加路由

 13、在數據庫中添加數據

14、請求接口

15、請求結果

jwt概述:

JWT(JSON Web Token)是一個非常輕巧的規范。這個規范允許我們使用JWT在用戶和服務器之間傳遞安全可靠的信息。一個JWT實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名。

1、安裝jwt

安裝地址:https://packagist.org/packages/tymon/jwt-auth

composer require tymon/jwt-auth

2.注冊服務提供者和別名

安裝完成后,在配置文件 config/app.php 中注冊服務提供者和別名:

'providers' => [
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class
]
 
'aliases' => [
    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
]

3、發布配置文件

在你項目根目錄運行如下命令,生成config\ jwt.php 的配置文件:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

4.在發布的配置中生成key:

此命令會在你的 .env 文件中新增一行 JWT_SECRET=secret。

php artisan jwt:secret

5.配置 api的驗證模式

在 config/auth.php 文件中,你需要將 guards中api['driver ']的值更新為 jwt:

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

6、修改Model 

如果需要使用 jwt-auth 作為用戶認證,我們需要對app/User模型進行修改,修改后的代碼如下:

<?php
 
namespace App;
 
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
 
class User extends Authenticatable implements JWTSubject
{
    use Notifiable;
 
    /**
     * The attributes that are mass assignable.
     *要驗證的字段
     * @var array
     */
    protected $fillable = [
        'openid', 'uuid',
    ];
 
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
 
    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
 
    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

注:此處有一個需要注意的地方如下圖 

 

 

 

7、 配置項詳解

文件位置:config/jwt.php,jwt.php中的各種詳解

<?php
 
return [
 
    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret
    |--------------------------------------------------------------------------
    | 用於加密生成 token 的 secret
    */
 
    'secret' => env('JWT_SECRET'),
 
    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    | 如果你在 .env 文件中定義了 JWT_SECRET 的隨機字符串
    | 那么 jwt 將會使用 對稱算法 來生成 token
    | 如果你沒有定有,那么jwt 將會使用如下配置的公鑰和私鑰來生成 token
    */
 
    'keys' => [
 
        /*
        |--------------------------------------------------------------------------
        | Public Key
        |--------------------------------------------------------------------------
        | 公鑰
        */
 
        'public' => env('JWT_PUBLIC_KEY'),
 
        /*
        |--------------------------------------------------------------------------
        | Private Key
        |--------------------------------------------------------------------------
        | 私鑰
        */
 
        'private' => env('JWT_PRIVATE_KEY'),
 
        /*
        |--------------------------------------------------------------------------
        | Passphrase
        |--------------------------------------------------------------------------
        | 私鑰的密碼。 如果沒有設置,可以為 null。
        */
 
        'passphrase' => env('JWT_PASSPHRASE'),
 
    ],
 
    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    | 指定 access_token 有效的時間長度(以分鍾為單位),默認為1小時,您也可以將其設置為空,以        
    | 產生永不過期的標記
    */
 
    'ttl' => env('JWT_TTL', 60),
 
    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    | 指定 access_token 可刷新的時間長度(以分鍾為單位)。默認的時間為 2 周。
    | 大概意思就是如果用戶有一個 access_token,那么他可以帶着他的 access_token 
    | 過來領取新的 access_token,直到 2 周的時間后,他便無法繼續刷新了,需要重新登錄。
    */
 
    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
 
    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    | 指定將用於對令牌進行簽名的散列算法。
    */
 
    'algo' => env('JWT_ALGO', 'HS256'),
 
    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    | 指定必須存在於任何令牌中的聲明。
    */
 
    'required_claims' => [
        'iss',
        'iat',
        'exp',
        'nbf',
        'sub',
        'jti',
    ],
 
    /*
    |--------------------------------------------------------------------------
    | Persistent Claims
    |--------------------------------------------------------------------------
    | 指定在刷新令牌時要保留的聲明密鑰。
    */
 
    'persistent_claims' => [
        // 'foo',
        // 'bar',
    ],
 
    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    | 為了使令牌無效,您必須啟用黑名單。如果您不想或不需要此功能,請將其設置為 false。
    */
 
    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
 
    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    | 當多個並發請求使用相同的JWT進行時,
    | 由於 access_token 的刷新 ,其中一些可能會失敗
    | 以秒為單位設置請求時間以防止並發的請求失敗。
    */
 
    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
 
    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    | 指定整個包中使用的各種提供程序。
    */
 
    'providers' => [
 
        /*
        |--------------------------------------------------------------------------
        | JWT Provider
        |--------------------------------------------------------------------------
        | 指定用於創建和解碼令牌的提供程序。
        */
 
        'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,
 
        /*
        |--------------------------------------------------------------------------
        | Authentication Provider
        |--------------------------------------------------------------------------
        | 指定用於對用戶進行身份驗證的提供程序。
        */
 
        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
 
        /*
        |--------------------------------------------------------------------------
        | Storage Provider
        |--------------------------------------------------------------------------
        | 指定用於在黑名單中存儲標記的提供程序。
        */
 
        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
 
    ],
 
];

8、自定義RefreshToken 中間件

實現效果:提供賬號密碼前來登錄。如果登錄成功,那么我會給前端返回一個 token ,設置在 header 中以請求需要用戶認證的路由。
如果用戶的令牌如果過期了,可以暫時通過此次請求,並在此次請求中刷新該用戶的 token,最后在響應頭中將新的 token 返回給前端,這樣子可以無痛的刷新 token ,用戶可以獲得一個很良好的體驗,所以開始動手寫代碼。無限刷新默認時間是兩周。

php artisan make:middleware RefreshToken
<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Auth;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 
 
class RefreshToken extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 檢查此次請求中是否帶有 token,如果沒有則拋出異常。
        $this->checkForToken($request);
 
        // 使用 try 包裹,以捕捉 token 過期所拋出的 TokenExpiredException  異常
        try {
            // 檢測用戶的登錄狀態,如果正常則通過
            if ($this->auth->parseToken()->authenticate()) {
                return $next($request);
            }
            throw new UnauthorizedHttpException('jwt-auth', '未登錄');
        } catch (TokenExpiredException $exception) {
            // 此處捕獲到了 token 過期所拋出的 TokenExpiredException 異常,我們在這里需要做的是刷新該用戶的 token 並將它添加到響應頭中
            try {
                // 刷新用戶的 token
                $token = $this->auth->refresh();
                // 使用一次性登錄以保證此次請求的成功
                Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
            } catch (JWTException $exception) {
                // 如果捕獲到此異常,即代表 refresh 也過期了,用戶無法刷新令牌,需要重新登錄。
                throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
            }
        }
 
        // 在響應頭中返回新的 token
        return $this->setAuthenticationHeader($next($request), $token);
 
    }
}

9、注冊RefreshToken 中間件

在app/Http/Kernel.php 添加如下代碼:

protected $routeMiddleware = [
        'refresh' => \App\Http\Middleware\RefreshToken::class,
]

10、更新異常處理的handler

由於我們構建的是 api 服務,所以我們需要更新一下 app/Exceptions/Handler.php 中的 render方法,自定義處理一些異常。

<?php
 
namespace App\Exceptions;
 
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 
class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];
 
    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];
 
    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }
 
    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        // 參數驗證錯誤的異常,我們需要返回 400 的 http code 和一句錯誤信息
        if ($exception instanceof ValidationException) {
            return response(['error' => array_first(array_collapse($exception->errors()))], 400);
        }
        // 用戶認證的異常,我們需要返回 401 的 http code 和錯誤信息
        if ($exception instanceof UnauthorizedHttpException) {
            return response($exception->getMessage(), 401);
        }
 
        return parent::render($request, $exception);
    }
}

11、添加新的控制器

創建控制器:

php artisan make:controller AuthController

添加如下代碼: 

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Transformers\UserTransformer;
 
class AuthController extends Controller
{
 
    /**
     * Get a JWT token via given credentials.
     *
     * @param  \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        // 驗證規則,由於業務需求,這里我更改了一下登錄的用戶名,使用手機號碼登錄
        $rules = [
            'email'   => [
                'required',
                'exists:users',
            ],
            'password' => 'required|string|min:6|max:20',
         ];
 
        // 驗證參數,如果驗證失敗,則會拋出 ValidationException 的異常
        $params = $this->validate($request, $rules);
 
       // 使用 Auth 登錄用戶,如果登錄成功,則返回 201 的 code 和 token,如果登錄失敗則返回
        return ($token = Auth::guard('api')->attempt($params))
            ? response(['token' => 'bearer ' . $token], 201)
            : response(['error' => '賬號或密碼錯誤'], 400);
    }
 
    /**
     * 處理用戶登出邏輯
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        Auth::guard('api')->logout();
 
        return response(['message' => '退出成功']);
    }
}

12、添加路由

在 routes/api.php 文件中,添加路由:

    Route::group(['prefix'=>'auth'],function($router) {
        $router->match(['post','get'],'login', 'AuthController@login');
        $router->post('logout', 'AuthController@logout');
 
    });
 
    Route::middleware('refresh')->group(function($router) {
        $router->get('profile','UserController@profile');
    });

 13、在數據庫中添加數據

DB::table('users')->insert(
    ['email' => 'john@example.com', 'password' => bcrypt('123456')]
);

注:此處密碼需用函數處理,因為Auth::attempt($params)是使用bcrypt()加密驗證的,否則會一直返回false

14、請求接口

 

 

15、請求結果

 

 注:若郵箱在數據庫中不存在,則也會返回false,因為驗證規則里面要求的是必須在數據表中存在,自己也可以修改 

轉載:https://blog.csdn.net/robin_sky/article/details/108230611  王老板


免責聲明!

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



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