JavaScript フロントエンド テクノロジが普及している現在、通常はフロントエンド呼び出しを提供するためにバックグラウンドで API を構築するだけでよく、バックエンドは設計のみです。フロントエンド モバイル アプリによって呼び出されます。ユーザー認証は Web アプリケーションの重要な部分です。API ベースのユーザー認証には、OAuth 2.0 と JWT (JSON Web Token) という 2 つの最適なソリューションがあります。
JWT (JSON Web Token) は非常に軽量な仕様です。この仕様により、JWT を使用してユーザーとサーバー間で安全で信頼できる情報を渡すことができるようになります。
JWT は実際には文字列であり、ヘッダー、ペイロード、署名の 3 つの部分で構成されます。
まず、ユーザー認証操作を JSON オブジェクトとして記述します。将来この JWT を受信するサーバーがこの JWT を理解するのに役立つ他の情報が追加されます。
{ "sub": "1", "iss": "http://localhost:8000/auth/login", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667"}
ここの最初の 6 フィールドは、JWT 標準によって定義されています。
これらの定義は標準規格に記載されています。
上記の JSON オブジェクトを Base64 エンコードして、次の文字列を取得します。
eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
この文字列を JWT ペイロードと呼びます。
Node.js を使用する場合は、Node.js パッケージのbase64url を使用して次の文字列を取得できます。
var base64url = require('base64url')var header = { "from_user": "B", "target_user": "A"}console.log(base64url(JSON.stringify(header)))
注: Base64 はエンコーディングです。元の形式に戻すことができます。これは暗号化プロセスではありません。
JWT にはヘッダーも必要です。ヘッダーは、JWT のタイプや使用されるアルゴリズムなど、JWT に関する最も基本的な情報を記述するために使用されます。サインなどこれは、JSON オブジェクトとして表すこともできます:
{ "typ": "JWT", "alg": "HS256"}
ここでは、これが JWT であり、使用する署名アルゴリズム (後述) が HS256 アルゴリズムであることを示します。
これも Base64 でエンコードされている必要があり、後続の文字列が JWT のヘッダーになります。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Connect上の 2 つのエンコードされた文字列をピリオドで (先頭から) 形成します:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
最後に、上記の文字を結合します 文字列は HS256 アルゴリズムを使用して暗号化されます。暗号化するときは、キー (秘密) も指定する必要があります:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
この方法で、暗号化されたコンテンツを取得できます:
wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
この部分は署名とも呼ばれます。
最後に、署名のこの部分も署名された文字列の後に結合され、完全な JWT が得られます。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ.wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
Composer を使用して jwt 拡張パッケージをインストールします:
composer require tymon/jwt-auth 0.5.*
インストールが完了したら、対応するサービスプロバイダーを config/app.php に登録する必要があります:
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class
次に、使用する必要がある対応するファサードを登録します:
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class
次に、対応する設定ファイルを公開します:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
最終的に生成されたキー:
php artisan jwt:generate
.env ファイルに追加する場合は、.env に JWT_SECRET フィールドを作成して実行しますキーコマンドを再度生成します。
config/jwt.php で、次のオプションを設定できます:
ユーザー トークンを作成する最も一般的な方法は、ログインによるユーザー認証を実装することです。成功すると、対応するユーザーのトークンが返されます。ここでは、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')); }}
場合によっては、ユーザー オブジェクト インスタンスを通じて直接トークンを作成することもできます:
// grab some user$user = User::first();$token = JWTAuth::fromUser($user);
さらに、TymonJWTAuthPayloadFactory インスタンスを使用することもできます。 (または JWTFactory ファサード) 任意のデータに基づいてトークンを作成します:
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];$payload = JWTFactory::make($customClaims);$token = JWTAuth::encode($payload);
メソッドチェーンを使用することもできます:
// add a custom claim with a key of `foo` and a value of ['bar' => 'baz']$payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();$token = JWTAuth::encode($payload);
用户登录成功之后,下一步就是发送一个包含token的请求来获取用户信息。
要通过http发送一个需要认证通过的请求,需要设置Authorization头:
Authorization: Bearer {yourtokenhere}
如果用户名/密码没有进行base64编码那么Apache似乎会摒弃Authorization头,要修复这一问题你可以添加如下代码到Apache配置文件:
RewriteEngine OnRewriteCond %{HTTP:Authorization} ^(.*)RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
或者将token信息包含到URL中:
http://api.mysite.com/me?token={yourtokenhere}
要从请求中获取token,你可以这么做:
// this will set the token on the objectJWTAuth::parseToken();// and you can continue to chain methods$user = JWTAuth::parseToken()->authenticate();
要获取该token值,你可以这么调用:
$token = JWTAuth::getToken();
如果token被设置则会返回,否则会尝试使用方法从请求中解析token,如果token未被设置或不能解析最终返回false。
当然如果需要的话你还可以手动设置token:
JWTAuth::setToken('foo.bar.baz');
从Token中获取认证用户:
// somewhere in your controllerpublic 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()); } // the token is valid and we have found the user via the sub claim return response()->json(compact('user'));}
jwt-auth扩展还提供了两个中间件 GetUserFromToken 和 RefreshToken ,前者用于在请求头和参数中检查是否包含token,并尝试对其解码,后者会再次从请求中解析token,并顺序刷新token(同时废弃老的token)并将其作为下一个响应的一部分。要使用这两个中间件,需要到 app/Http/Kernel.php 下的 $routeMiddleware 属性中注册它们:
protected $routeMiddleware = [ ... 'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken', 'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',];
JWT让用户认证变得简单和安全,token会被保存到本地的 storage/web 或Cookie中,使用JWT,基于API的用户认证将不再困难。