摘要:默認的時間為周。大概意思就是如果用戶有一個,那么他可以帶著他的過來領取新的,直到周的時間后,他便無法繼續刷新了,需要重新登錄。指定在刷新令牌時要保留的聲明密鑰。為了使令牌無效,您必須啟用黑名單。指定用于對用戶進行身份驗證的提供程序。
最近在做一個公司的項目,前端使用 Vue.js,后端使用 Laravel 構建 Api 服務,用戶認證的包本來是想用 Laravel Passport 的,但是感覺有點麻煩,于是使用了 jwt-auth 。
安裝jwt-auth 最新版本是 1.0.0 rc.1 版本,已經支持了 Laravel 5.5。如果你使用的是 Laravel 5.5 版本,可以使用如下命令安裝。根據評論區 @tradzero 兄弟的建議,如果你是 Laravel 5.5 以下版本,也推薦使用最新版本,RC.1 前的版本都存在多用戶token認證的安全問題。
$ composer require tymon/jwt-auth 1.0.0-rc.1配置 添加服務提供商
將下面這行添加至 config/app.php 文件 providers 數組中:
app.php
"providers" => [ ... TymonJWTAuthProvidersLaravelServiceProvider::class, ]發布配置文件
在你的 shell 中運行如下命令發布 jwt-auth 的配置文件:
shell
$ php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"
此命令會在 config 目錄下生成一個 jwt.php 配置文件,你可以在此進行自定義配置。
生成密鑰jwt-auth 已經預先定義好了一個 Artisan 命令方便你生成 Secret,你只需要在你的 shell 中運行如下命令即可:
shell
$ php artisan jwt:secret
此命令會在你的 .env 文件中新增一行 JWT_SECRET=secret。
配置 Auth guard在 config/auth.php 文件中,你需要將 guards/driver 更新為 jwt:
auth.php
"defaults" => [ "guard" => "api", "passwords" => "users", ], ... "guards" => [ "api" => [ "driver" => "jwt", "provider" => "users", ], ],
只有在使用 Laravel 5.2 及以上版本的情況下才能使用。更改 Model
如果需要使用 jwt-auth 作為用戶認證,我們需要對我們的 User 模型進行一點小小的改變,實現一個接口,變更后的 User 模型如下:
User.php
getKey(); } /** * Return a key value array, containing any custom claims to be added to the JWT. * * @return array */ public function getJWTCustomClaims() { return []; } }配置項詳解
jwt.php
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" => TymonJWTAuthProvidersJWTNamshi::class, /* |-------------------------------------------------------------------------- | Authentication Provider |-------------------------------------------------------------------------- | | 指定用于對用戶進行身份驗證的提供程序。 | */ "auth" => TymonJWTAuthProvidersAuthIlluminate::class, /* |-------------------------------------------------------------------------- | Storage Provider |-------------------------------------------------------------------------- | | 指定用于在黑名單中存儲標記的提供程序。 | */ "storage" => TymonJWTAuthProvidersStorageIlluminate::class, ], ];自定義認證中間件
先來說明一下我想要達成的效果,我希望用戶提供賬號密碼前來登錄。如果登錄成功,那么我會給前端頒發一個 access _token ,設置在 header 中以請求需要用戶認證的路由。
同時我希望如果用戶的令牌如果過期了,可以暫時通過此次請求,并在此次請求中刷新該用戶的 access _token,最后在響應頭中將新的 access _token 返回給前端,這樣子可以無痛的刷新 access _token ,用戶可以獲得一個很良好的體驗,所以開始動手寫代碼。
執行如下命令以新建一個中間件:
php artisan make:middleware RefreshToken
中間件代碼如下:
RefreshToken.php
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); } }設置 Axios 攔截器
我選用的 HTTP 請求套件是 axios。為了達到無痛刷新 token 的效果,我們需要對 axios 定義一個攔截器,用以接收我們刷新的 Token,代碼如下:
app.js
import Vue from "vue" import router from "./router" import store from "./store" import iView from "iview" import "iview/dist/styles/iview.css" Vue.use(iView) new Vue({ el: "#app", router, store, created() { // 自定義的 axios 響應攔截器 this.$axios.interceptors.response.use((response) => { // 判斷一下響應中是否有 token,如果有就直接使用此 token 替換掉本地的 token。你可以根據你的業務需求自己編寫更新 token 的邏輯 var token = response.headers.authorization if (token) { // 如果 header 中存在 token,那么觸發 refreshToken 方法,替換本地的 token this.$store.dispatch("refreshToken", token) } return response }, (error) => { switch (error.response.status) { // 如果響應中的 http code 為 401,那么則此用戶可能 token 失效了之類的,我會觸發 logout 方法,清除本地的數據并將用戶重定向至登錄頁面 case 401: return this.$store.dispatch("logout") break // 如果響應中的 http code 為 400,那么就彈出一條錯誤提示給用戶 case 400: return this.$Message.error(error.response.data.error) break } return Promise.reject(error) }) } })
Vuex 內的代碼如下:
store/index.js
import Vue from "vue" import Vuex from "vuex" import axios from "axios" Vue.use(Vuex) export default new Vuex.Store({ state: { name: null, avatar: null, mobile: null, token: null, remark: null, auth: false, }, mutations: { // 用戶登錄成功,存儲 token 并設置 header 頭 logined(state, token) { state.auth = true state.token = token localStorage.token = token }, // 用戶刷新 token 成功,使用新的 token 替換掉本地的token refreshToken(state, token) { state.token = token localStorage.token = token axios.defaults.headers.common["Authorization"] = state.token }, // 登錄成功后拉取用戶的信息存儲到本地 profile(state, data) { state.name = data.name state.mobile = data.mobile state.avatar = data.avatar state.remark = data.remark }, // 用戶登出,清除本地數據 logout(state){ state.name = null state.mobile = null state.avatar = null state.remark = null state.auth = false state.token = null localStorage.removeItem("token") } }, actions: { // 登錄成功后保存用戶信息 logined({dispatch,commit}, token) { return new Promise(function (resolve, reject) { commit("logined", token) axios.defaults.headers.common["Authorization"] = token dispatch("profile").then(() => { resolve() }).catch(() => { reject() }) }) }, // 登錄成功后使用 token 拉取用戶的信息 profile({commit}) { return new Promise(function (resolve, reject) { axios.get("profile", {}).then(respond => { if (respond.status == 200) { commit("profile", respond.data) resolve() } else { reject() } }) }) }, // 用戶登出,清除本地數據并重定向至登錄頁面 logout({commit}) { return new Promise(function (resolve, reject) { commit("logout") axios.post("auth/logout", {}).then(respond => { Vue.$router.push({name:"login"}) }) }) }, // 將刷新的 token 保存至本地 refreshToken({commit},token) { return new Promise(function (resolve, reject) { commit("refreshToken", token) }) }, } })更新異常處理的 Handler
由于我們構建的是 api 服務,所以我們需要更新一下 app/Exceptions/Handler.php 中的 render
方法,自定義處理一些異常。
Handler.php
array_first(array_collapse($exception->errors()))], 400); } // 用戶認證的異常,我們需要返回 401 的 http code 和錯誤信息 if ($exception instanceof UnauthorizedHttpException) { return response($exception->getMessage(), 401); } return parent::render($request, $exception); } }
更新完此方法后,我們上面自定義的中間件里拋出的異常和我們下面參數驗證錯誤拋出的異常都會被轉為指定的格式拋出。
使用現在,我們可以在我們的 routes/api.php 路由文件中新增幾條路由來測試一下了:
api.php
Route::prefix("auth")->group(function($router) { $router->post("login", "AuthController@login"); $router->post("logout", "AuthController@logout"); }); Route::middleware("refresh.token")->group(function($router) { $router->get("profile","UserController@profile"); });
在你的 shell 中運行如下命令以新增一個控制器:
$ php artisan make:controller AuthController
打開此控制器,寫入如下內容
[ "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 IlluminateHttpJsonResponse */ public function logout() { Auth::guard("api")->logout(); return response(["message" => "退出成功"]); } }
然后我們進入 tinker:
$ php artisan tinker
執行以下命令來創建一個測試用戶,我這里的用戶名是用的是手機號碼,你可以自行替換為郵箱。別忘了設置命名空間喲:
>>> namespace AppModels; >>> User::create(["name" => "Test","mobile" => 17623239881,"password" => bcrypt("123456")]);
正確執行結果如下圖:
然后打開 Postman 來進行 api 測試
正確的請求結果如下圖:
可以看到我們已經成功的拿到了 token,接下來我們就去驗證一下刷新 token 吧
如圖可以看到我們已經拿到了新的 token,接下來的事情便會交由我們前面設置的 axios 攔截器處理,它會將本地的 token 替換為此 token。
版本科普感覺蠻多人對版本沒什么概念,所以在這里科普下常見的版本。
α(Alpha)版
? 這個版本表示該 Package 僅僅是一個初步完成品,通常只在開發者內部交流,也有很少一部分發布給專業測試人員。一般而言,該版本軟件的 Bug 較多,普通用戶最好不要安裝。
β(Beta)版
該版本相對于 α(Alpha)版已有了很大的改進,修復了嚴重的錯誤,但還是存在著一些缺陷,需要經過大規模的發布測試來進一步消除。通過一些專業愛好者的測試,將結果反饋給開發者,開發者們再進行有針對性的修改。該版本也不適合一般用戶安裝。
RC/ Preview版
RC 即 Release Candidate 的縮寫,作為一個固定術語,意味著最終版本準備就緒。一般來說 RC 版本已經完成全部功能并清除大部分的 BUG。一般到了這個階段 Package 的作者只會修復 Bug,不會對軟件做任何大的更改。
普通發行版本
一般在經歷了上面三個版本后,作者會推出此版本。此版本修復了絕大部分的 Bug,并且會維護一定的時間。(時間根據作者的意愿而決定,例如 Laravel 的一般發行版本會提供為期一年的維護支持。)
LTS(Long Term Support) 版
該版本是一個特殊的版本,和普通版本旨在支持比正常時間更長的時間。(例如 Laravel 的 LTS 版本會提供為期三年的 維護支持。)
結語jwt-auth 確實是一個很棒的用戶認證 Package,配置簡單,使用方便。
文章結束,感謝閱讀。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/26275.html
showImg(https://segmentfault.com/img/bV6aHV?w=1280&h=800); 社區優秀文章 Laravel 5.5+passport 放棄 dingo 開發 API 實戰,讓 API 開發更省心 - 自造車輪。 API 文檔神器 Swagger 介紹及在 PHP 項目中使用 - API 文檔撰寫方案 推薦 Laravel API 項目必須使用的 8 個...
摘要:最近在寫一個前后端分離項目,本來想用開發的,但是略感笨重,于是想到了的和新出的。更改看守器驅動將配置文件中授權看守器的的選項改為。然后在你的前端請求里需要加入一個參數然后在你需要使用到認證的路由里使用中間件,一切就大功告成啦 最近在寫一個前后端分離項目,本來想用 Jwt-auth + Dingo 開發的,但是略感笨重,于是想到了 Laravel 的 Passport 和 5.5 新出的...
摘要:此處做刷新處理具體代碼可以參考必須需要登錄驗證的接口在響應頭中返回新的問題解決。 在做 API 開發時,不可避免會涉及到登錄驗證,我使用的是jwt-auth 在登錄中會經常遇到一個token過期的問題,在config/jwt.php默認設置中,這個過期時間是一個小時,不過為了安全也可以設置更小一點,我設置了為五分鐘。 五分鐘過期,如果就讓用戶去登錄,這種體驗會讓用戶直接拋棄你的網站...
摘要:最近項目做認證,最終技術選型決定使用,項目框架使用的是,使用有比較方便使用的開源包。使用安裝,使用的框架版本為,最新穩定版本為。 最近項目做API認證,最終技術選型決定使用JWT,項目框架使用的是laravel,laravel使用JWT有比較方便使用的開源包:jwt-auth。 使用composer安裝jwt-auth,laravel使用的框架版本為5.0,jwt-auth最新穩定版本...
摘要:模糊授權,跟上面的認證碼授權類似,不同的是,我們的資源服務器,返回的直接就是準入令牌,而不是認證碼。 本文來自pilishen.com----原文鏈接; 歡迎來和pilishen一起學習php&Laravel;學習群:109256050 OAuth2是一個安全框架,控制著程序受保護部分的準入,主要是控制不同的客戶端如何來調取API,保證它們在請求相應資源的時候有相應的權限。 Larav...
閱讀 2058·2021-11-11 16:54
閱讀 1039·2021-10-12 10:12
閱讀 377·2019-08-30 15:43
閱讀 646·2019-08-29 13:15
閱讀 1075·2019-08-29 13:12
閱讀 1524·2019-08-26 12:09
閱讀 1655·2019-08-26 10:24
閱讀 2250·2019-08-26 10:15