laravel 5.7 安裝 jwt-auth,jwt-auth 文件翻譯
阿新 • • 發佈:2018-12-19
laravel 5.7 安裝 jwt-auth(預設安裝的是 0.5.12 版本) github 地址: https://github.com/tymondesigns/jwt-auth 舊版文件: https://github.com/tymondesigns/jwt-auth/wiki 新版文件: https://jwt-auth.readthedocs.io/en/develop/(這個是 1.0.0 版本的文件) 安裝(Installation) 安裝包 composer require tymon/jwt-auth 新增服務提供者 編輯 config/app.php,在 "providers" 新增: 'Tymon\JWTAuth\Providers\JWTAuthServiceProvider', 新增 Facades 編輯 config/app.php,在 "alias" 新增: 'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth', 'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory', 釋出配置檔案: php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider" 生成 secret key php artisan jwt:generate 安裝完成後,執行 php artisan jwt:generate,會報錯: 1.Method Tymon\JWTAuth\Commands\JWTGenerateCommand::handle() does not exist 解決方法,這個需要修改原始碼: 編輯 vendor/tymon/jwt-auth/src/Commands/JWTGenerateCommand.php,新增: /** * Compatiblity with Laravel >= 5.5 */ public function handle() { $this->fire(); } 2.如果未新增服務提供者,直接執行該命令,可能也會報錯! There are no commands defined in the "jwt" namespace 解決方法: 就是上面的新增服務提供者 配置(Configuration) secret(secret key) - 祕鑰 用來簽名 token 的祕鑰。作者將祕鑰與 Laravel 的 APP_KEY 分開,以便開發者可以獨立地修改它們。 提供了一個 artisan 命令,為我們生成一個隨機祕鑰。(php artisan jwt:generate) ttl(token time to live) - token 生存時間 token 的有效時間,以分鐘為單位。建議儘可能設定短點,尤其是當我們也使用 token 重新整理機制。 refresh_ttl(refresh time to live) - refresh 生存時間 可以重新整理 token 的有效時間,以分鐘為單位。例如,如果設定為 2周,那麼只能在 2周 內,重新整理對應的 token,否則將會丟擲 TokenExpiredException 異常。如果超過了重新整理的有效時間,必須生成一個全新的 token,這意味著使用者需要重新登入。 注:ttl 和 refresh_ttl,用於保持使用者的登入狀態 algo(hashing algorithm) - hash 演算法 用於簽名 token 的演算法,保留預設值即可 user(user model path) - 使用者模型路徑 應該指向我們專案的 User 類的名稱空間路徑 identifier(user identifier) - 使用者標識 從 token 的主題宣告中,根據什麼標識來檢索使用者(一般是 id) required_claims(required claims) 這些宣告必須存在於 token 的 payload 中,否則將丟擲 TokenInvalidException 異常(會檢測 token 的 payload 是否存在這些宣告) blacklist_enabled(blacklist enabled) 如果設定為 false,將無法使 token 失效。雖然我們仍然可以重新整理令牌,但是之前的令牌仍舊有效,因此這樣做非常不安全。但對於非常簡單的實現,可能不需要額外的開銷(重新整理 token 等),我們可以配置它。 providers jwt-auth 包已經有一些具體實現,可用來實現各種需求。只要遵循相關介面,我們就可以覆蓋這些具體實現。 providers.user 指定基於主題宣告,來查詢使用者的實現。 providers.jwt 完成 token 的編碼和解碼的繁重工作 providers.auth 通過憑證或 id 來認證使用者 providers.storage 用於驅動黑名單,並存儲 token 直到過期。 建立 tokens(Creating Tokens) jwt-auth 包為我們提供了建立 token 的多種方法。有簡單的方法,如果你想更好的控制,也有更進一步的方法。 開箱即用(out of box),有許多必須的宣告,雖然這些都可以配置: sub(Subject) - 包含 token 的識別符號(預設是使用者 ID) iat(Issued At) - token 釋出時間(unix 時間戳) exp(Expiry) - token 過期日期(unix 時間戳) nbf(Not Before) - 可以使用 token 的最早時間點(unix 時間戳) iss(Issuer) - token 釋出者(預設為請求的 url) jti(JWT Id) - token 的唯一識別符號(sub 和 iat 宣告的 md5 值) aud(Audience) - token 的目標受眾(預設不需要) 也允許自定義宣告。稍後會介紹。 建立一個基於使用者憑證的 token 建立 token 的最常用方法是,通過使用者的登入憑證,來認證使用者。如果認證成功,則返回一個與該使用者相關的 token。例如,假設我們有一個 Laravel AuthenticateController use JWTAuth; use Tymon\JWTAuth\Exceptions\JWTException; class AuthenticateController extends Controller { public function authenticate(Request $request) { // grab credentials from the request $credentials = $request->only('email', 'password'); try { // attempt to verify the credentials and create a token for the user if (! $token = JWTAuth::attempt($credentials)) { return response()->json(['error' => 'invalid_credentials'], 401); } } catch (JWTException $e) { // something went wrong whilst attempting to encode the token return response()->json(['error' => 'could_not_create_token'], 500); } // all good so return the token return response()->json(compact('token')); } } 建立一個基於使用者物件的 token 我們可以跳過使用者認證,只傳遞一個使用者物件 $user = User::first(); $token = JWTAuth::fromUser($user); 上面的 2 個方法也有第二個引數,可以傳遞一個 '自定義宣告' 的陣列 $customClaims = ['foo' => 'bar', 'baz' => 'bob']; JWTAuth::attempt($credentials, $customClaims); JWTAuth::fromUser($user, $customClaims); 在解碼 token 時,這些自定義宣告,將和其他宣告一起提供。 注意:新增大量的自定義宣告,將增加 token 的大小 建立一個基於任意你喜歡的內容的 token 作者給我們提供了對底層類和方法的訪問,來提供高階的、可自定義的功能。 示例使用了內建的 'Tymon\JWTAuth\PayloadFactory' 例項(或者使用 JWTFactory 門面): $customClaims = ['foo' => 'bar', 'baz' => 'bob']; $payload = JWTFactory::make($customClaims); $token = JWTAuth::encode($payload); 也可以在 'Tymon\JWTAuth\PayloadFactory' 例項上鍊式呼叫宣告(或者使用 JWTFactory 門面): $payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz']); $token = JWTAuth::encode($payload); 認證(Authentication) 一旦使用者使用他們的憑證登入,下一步將使用 token 發起一個後續請求,來檢索使用者詳情,以便我們可以將其顯示為已登入。 使用內建方法,通過 http 發起認證請求,我們需要設定一個 Authorization 請求頭,如下所示: Authorization: Bearer {yourtokenhere} Apache 使用者需要注意: Apache 好像會丟棄 Authorization 請求頭,如果該請求頭不是 base64 編碼的 user/pass 組合。為了解決此問題,我們可以在 apache 配置檔案中新增一下內容: RewriteEngine On RewriteCond %{HTTP:Authorization} ^(.*) RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] 或者,我們可以通過在查詢字串中包含 token 來實現: http://api.mysite.com/me?token={yourtokenhere} 為了從請求中獲取 token,我們可以: // 會設定 token 到返回的物件中 JWTAuth::parseToken(); // 接著,我們可以繼續鏈式呼叫方法 $user = JWTAuth::parseToken()->authenticate(); 為了獲取 token 的值,我們可以呼叫: $token = JWTAuth::getToken(); 如果設定了一個 token,則會返回 token,否則(為方便起見),它將使用上述方法,嘗試從請求中解析 token,如果沒有設定 token 或 沒有 token 可以被解析,最終返回 false。 當然,如果在我們的程式中有其他入口點,我們也可以根據需要手動設定 token。例如: JWTAuth::setToken('foo.bar.baz'); 從 token 中檢索認證過的使用者 public function getAuthenticatedUser() { try { if(! $user = JWTAuth::parseToken()->authenticate()){ return response()->json('user_not_found', 404); } } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) { return response()->json(['token_expired'], $e->getStatusCode()); } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) { return response()->json(['token_invalid'], $e->getStatusCode()); } catch (Tymon\JWTAuth\Exceptions\JWTException $e) { return response()->json(['token_absent'], $e->getStatusCode()); } return response()->json(compact('user')); } 如果不喜歡內聯捕獲多個異常的方法,我們可以隨意使用 Laravel 新增全域性異常處理程式。 在 app/Exceptions/Handler.php 中,將下面程式碼新增到 render() 方法: public function render($request, Exception $e) { if ($e instanceof Tymon\JWTAuth\Exceptions\TokenExpiredException) { return response()->json(['token_expired'], $e->getStatusCode()); } else if ($e instanceof Tymon\JWTAuth\Exceptions\TokenInvalidException) { return response()->json(['token_invalid'], $e->getStatusCode()); } return parent::render($request, $e); } 中介軟體和過濾器 如果我們使用的是 Laravel 5,可以使用內建的 2 箇中間件: GetUserFromToken 檢查請求頭和查詢字串(正如上面解釋過的)是否存在 token,並嘗試解碼 token。如上所述,同樣的事件被觸發。 RefreshToken 此中介軟體將再次嘗試從請求中解析 token,然後將重新整理 token(從而使舊 token 失效),並將其作為下一次響應的一部分返回。這實際上產生了單個使用 token 流,如果 token 被洩露,這種方式會減少攻擊,因為它僅對單個請求有效。 為了使用這 2 箇中間件,我們需要將它們註冊到 app/Http/Kernel.php 裡的 $routeMIddleware 屬性: protected $routeMiddleware = [ ... 'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken', 'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken', ]; 附 2 篇優秀文章(沒看,但應該不錯): https://laravel-chiona.org/articles/10885/full-use-of-jwt https://laravel-china.org/articles/17883 ----------------------------------------------------------------------------- 後記: 發現不指定版本號,安裝的是 0.5.12 版本 我們專案中,打算使用最新版:1.0.0-rc.3 composer require tymon/jwt-auth:1.0.0-rc.3 文件地址: https://jwt-auth.readthedocs.io/en/develop/ 翻譯: Laravel 安裝: 通過 composer 安裝: composer require tymon/jwt-auth:1.0.0-rc.3 新增服務提供者: 編輯 config/app.php,在 "providers" 新增: Tymon\JWTAuth\Providers\LaravelServiceProvider::class, 釋出配置檔案: php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" 生成 secret key: php artisan jwt:secret .env 檔案下,會新增 JWT_SECRET 快速開始: 更新使用者模型: User 模型,實現 Tymon\JWTAuth\Contracts\JWTSubject,並新增 getJWTIdentifier() 和 getJWTCustomClaims() 2 個方法 例如: <?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; // Rest omitted for brevity /** * 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 []; } } 配置 Auth guard: config/auth.php 'defaults' => [ 'guard' => 'api', // 使用 api guard 'passwords' => 'users', ], ... 'guards' => [ 'api' => [ // 定義 api guard 'driver' => 'jwt', // 使用 jwt 'provider' => 'users', ], ], 中介軟體: auth:api 控制器內使用: <?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Auth; use App\Http\Controllers\Controller; class AuthController extends Controller { /** * Create a new AuthController instance. * * @return void */ public function __construct() { $this->middleware('auth:api', ['except' => ['login']]); // 中介軟體 } /** * Get a JWT via given credentials. * * @return \Illuminate\Http\JsonResponse */ public function login() { $credentials = request(['email', 'password']); if (! $token = auth()->attempt($credentials)) { // api 認證,通過後得到 token return response()->json(['error' => 'Unauthorized'], 401); } return $this->respondWithToken($token); // 返回 token 給客戶端 } /** * Get the authenticated User. * * @return \Illuminate\Http\JsonResponse */ public function me() { return response()->json(auth()->user()); // 通過 api 認證得到使用者資訊 } /** * Log the user out (Invalidate the token). * * @return \Illuminate\Http\JsonResponse */ public function logout() { auth()->logout(); // api 退出 return response()->json(['message' => 'Successfully logged out']); } /** * Refresh a token. * * @return \Illuminate\Http\JsonResponse */ public function refresh() { return $this->respondWithToken(auth()->refresh()); // 重新整理 token,返回給客戶端 } /** * Get the token array structure. * * @param string $token * * @return \Illuminate\Http\JsonResponse */ protected function respondWithToken($token) { return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ]); } } 認證請求: 通過 HTTP 給伺服器傳送 token,有以下幾種方法: 認證頭: Authorization: Bearer eyJhbGciOiJIUzI1NiI... 查詢字串引數: http://example.dev/me?token=eyJhbGciOiJIUzI1NiI... Post 引數: 暫無 Cookies: 暫無 Laravel 路由引數: 暫無 Auth Guard: Auth Guard 例項上,可用以下方法: attempt() - 通過使用者身份憑證,來認證使用者 $token = auth()->attempt($credentials); 成功返回 jwt token,失敗返回 null login() - 傳遞一個使用者例項,來登入使用者 $user = User::first(); $token = auth()->login($user); user() - 獲取當前認證的使用者 $user = auth()->user(); 如果使用者未認證,返回 null userOrFail() - 獲取當前認證的使用者,否則丟擲異常 try { $user = auth()->userOrFail(); } catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) { // do something } logout() - 使用者退出,會使當前 token 失效,並刪除認證的使用者 auth()->logout(); // 第一個引數傳遞 true,強制 token 新增到 "forever" 黑名單 auth()->logout(true); refresh() - 重新整理 token,會令當前 token 失效,生成一個新 token $newToken = auth()->refresh(); // 第一個引數傳遞 true,強制此 token 新增到 "forever" 黑名單 // 第二個引數傳遞 true,會重置新 token 的宣告(claims) $newToken = auth()->refresh(true, true); invalidate() - 使當前 token 失效(新增到黑名單) auth()->invalidate(); // 第一個引數傳遞 true,強制 token 新增到 "forever" 黑名單 auth()->invalidate(true); tokenById() - 基於給定使用者 ID,獲取 token $token = auth()->tokenById(32451); payload() - 獲取原生的 JWT payload $payload = auth()->payload(); // 之後可以直接訪問宣告 $payload->get('sub'); // 123 $payload['jti']; // 'xxx' $payload('exp'); // 123456 $payload->toArray(); // ['sub' => 123, 'exp' => 123456, 'jti' => 'xxx'] validate() - 認證使用者身份憑證 if(auth()->validate($credentials)){ // 認證通過 } 進一步使用: 新增自定義宣告: $token = auth()->claims(['foo' => 'bar'])->attempt($credentials); 顯示設定 token: $user = auth()->setToken('xxx')->user(); 顯示設定請求例項: $user = auth()->setRequest($request)->user(); 覆蓋 token ttl: $token = auth()->setTTL(7200)->attempt($credentials); 配置: 和舊版本的配置不同,官方還未提供文件,看 config/jwt.php 檔案註釋即可