在例如身份驗證場景中,用戶一旦登陸,接下來的每一個請求都會包含jwt,用來驗證身份信息。因爲通訊雙方使用jwt對數據進行編碼,它的信息是通過簽名的,因此能夠確保信息的安全性.算法
cookie缺點mongodb
jwt優勢json
咱們先來看看JWT的是怎麼生成的?JWT是一種規範.他由下面的一種結構構造而成.跨域
header.payload.signature
複製代碼
JWT 的 Header 部分包含有關如何計算 JWT 簽名的信息,是一個如下形式的 JSON 對象:瀏覽器
{
"typ": "JWT",
"alg": "HS256"
}
複製代碼
在上面的 JSON 中,「typ」鍵的值指定對象是JWT,「alg」鍵的值指定用於建立 JWT 簽名的算法。 在示例中,咱們使用 HMAC-SHA256算法(一種使用密鑰的散列算法)來計算簽名(在步驟3中會更詳細的介紹)。安全
建立 PAYLOADbash
JWT 的 payload 部分時是存儲在 JWT 內的數據。在咱們的示例中,身份驗證服務器建立一個JWT,其中存儲有用戶信息,特別是用戶ID。服務器
{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}
複製代碼
在咱們的示例中,咱們只將一個聲明放入 payload 中。 你能夠根據須要添加任意數量的聲明。JWT 規定了7個官方字段,供選用。cookie
iss (issuer):簽發人
exp (expiration time):過時時間
sub (subject):主題
aud (audience):受衆
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號
複製代碼
建立 SIGNATURE網絡
// signature algorithm
data = base64urlEncode( header ) + 「.」 + base64urlEncode( payload )
hashedData = hash( data, secret )
signature = base64urlEncode( hashedData )
複製代碼
是的JWT是經過不斷的計算來進行校驗的.
這裏的性能問題, 指的是該結構帶來的請求數據變多的問題. 在jwt的使用場景中, 全部的請求都須要完整帶上jwt的數據. 因爲payload這部分的數據僅僅是base64後的數據, 並無作任何處理. 因此, 若是在payload中增長過多的數據, 就會致使jwt的結構變大, 從而致使請求的效率下降.
若是把token存在redie或者mongodb中也是一個不錯的選擇. 咱們該如何作呢?
下面我以Laravel
遷移文件以下
$table->bigIncrements('id');
$table->integer('user_id');
$table->string('token', 191)->default('')->comment('token');
$table->integer('expiry_time')->default(0)->comment('到期時間');
$table->string('ua')->default('')->comment('瀏覽器');
$table->string('ip')->default('')->comment('使用者ip');
$table->timestamps();
複製代碼
在經過登陸校驗成功的時候生成Token
$token = md5(uniqid(md5(microtime(true)), true));
$token = sha1($token);
UserTokens::create([
'user_id' => $user->id,
'token' => $token,
'ip' => request()->ip(),
'expiry_time' => time() + 2 * 60 * 60,
'ua' => request()->userAgent()
]);
return $token;
複製代碼
在須要的路由注入中間件攔截器
class Authenticate extends Controller
{
public function handle(Request $request, \Closure $next)
{
$Authorization = $request->header('Authorization');
if (!$Authorization) {
return response($this->error('請登陸', 40001));
}
$token = explode(' ', $Authorization);
$token = $token[1];
if ($this->validateToken($token)) {
return $this->validateToken($token);
}
return $next($request);
}
protected function validateToken($token)
{
$dbToken = UserTokens::where('token', $token)->first();
if ($dbToken->token != $token) {
return response($this->error('您的信息有誤!', 40010));
} else if ($dbToken->expiry_time < time()) {
return response($this->error('token有效期到期', 40020));
} else if ($dbToken->ua != \request()->userAgent()) {
return response($this->error('您的信息有誤!', 40010));
} else if ($dbToken->ip != \request()->ip()) {
return response($this->error('您的信息有誤!', 40010));
} else {
return false;
}
}
}
複製代碼
這種方式也是能夠的.