使用Laravel集成JWT認證開發RestfulApi

在使用跨平臺應用程序時, API 是一個很是不錯的選擇。 除了網站,您的產品可能還有 Android 和 iOS 應用程序。 在這種狀況下, API 也是一樣出色的,由於您能夠在不更改任何後端代碼的狀況下編寫不一樣的前端。 使用 API 時,只需使用一些參數點擊 GET , POST 或其餘類型的請求,服務器就會返回 JSON (JavaScript Object Notation) 格式的一些數據,這些數據由客戶端應用程序處理。php

說明前端

咱們先寫下咱們的應用程序詳細信息和功能。 咱們將使用 JWT 身份驗證在 laravel 中使用 restful API 構建基本用戶產品列表。laravel

A User 將會使用如下功能數據庫

  • 註冊並建立一個新賬戶
  • 登陸到他們的賬戶
  • 註銷和丟棄 token 並離開應用程序
  • 獲取登陸用戶的詳細信息
  • 檢索可供用戶使用的產品列表
  • 按 ID 查找特定產品
  • 將新產品添加到用戶產品列表中
  • 編輯現有產品詳細信息
  • 從用戶列表中刪除現有產品

A User 必填json

  • name
  • email
  • password

A Product 必填後端

  • name
  • price
  • quantity

建立新的項目

經過運行下面的命令,咱們就能夠開始並建立新的 Laravel 項目。api

composer create-project --prefer-dist laravel/laravel jwt數組

這會在名爲 jwt 的目錄下建立一個新的 Laravel 項目。服務器

配置 JWT 擴展包restful

咱們會使用 tymondesigns/jwt-auth 擴展包來讓咱們在 Laravel 中使用 JWT。

安裝 tymon/jwt-auth 擴展包

讓咱們在這個 Laravel 應用中安裝這個擴展包。若是您正在使用 Laravel 5.5 或以上版本,請運行如下命令來獲取 dev-develop 版本的 JWT 包:

composer require tymon/jwt-auth:dev-develop --prefer-source

  

若是您正在使用 Laravel 5.4 或如下版本,那麼要運行下面這條命令:

composer require tymon/jwt-auth

  

對於 Laravel 版本 低於 5.5 的應用,您還要在 config/app.php 文件中設置服務提供者和別名。

'providers' => [
 ....
 Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
 ....
],
'aliases' => [
 ....
 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
 'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
 ....
],

  

若是您的 Laravel 版本爲 5.5 或以上,Laravel 會進行「包自動發現」。

發佈配置文件

對於 5.5 或以上版本 的 Laravel,請使用下面這條命令來發布配置文件:

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

  

對於以前 以前版本的 Laravel,那麼應該運行下面這條命令:

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

  

上面的命令會生成 config/jwt.php 配置文件。除去註釋部分,配置文件會像這樣:

<?php
return [
 'secret' => env('JWT_SECRET'),
 'keys' => [
 'public' => env('JWT_PUBLIC_KEY'),
 'private' => env('JWT_PRIVATE_KEY'),
 'passphrase' => env('JWT_PASSPHRASE'),
 ],
 'ttl' => env('JWT_TTL', 60),
 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
 'algo' => env('JWT_ALGO', 'HS256'),
 'required_claims' => [
 'iss',
 'iat',
 'exp',
 'nbf',
 'sub',
 'jti',
 ],
 'persistent_claims' => [
 // 'foo',
 // 'bar',
 ],
 'lock_subject' => true,
 'leeway' => env('JWT_LEEWAY', 0),
 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
 'decrypt_cookies' => false,
 'providers' => [
 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
 ],
];

  

生成 JWT 密鑰

JWT 令牌經過一個加密的密鑰來簽發。對於 Laravel 5.5 或以上版本,運行下面的命令來生成密鑰以便用於簽發令牌。

php artisan jwt:secret

  

Laravel 版本低於 5.5 的則運行:

php artisan jwt:generate

  

這篇教程使用 Laravel 5.6。教程中接下來的步驟只在 5.5 和 5.6 中測試過。可能不適用於 Laravel 5.4 或如下版本。您能夠閱讀 針對舊版本 Laravel 的文檔。

註冊中間件

JWT 認證擴展包附帶了容許咱們使用的中間件。在 app/Http/Kernel.php 中註冊 auth.jwt 中間件:

protected $routeMiddleware = [
 ....
 'auth.jwt' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
];

  

這個中間件會經過檢查請求中附帶的令牌來校驗用戶的認證。若是用戶未認證,這個中間件會拋出 UnauthorizedHttpException 異常。

設置路由

開始以前,咱們將爲全部本教程討論的點設置路由。打開 routes/api.php 並將下面的路由複製到您的文件中。

Route::post('login', 'ApiController@login');
Route::post('register', 'ApiController@register');
Route::group(['middleware' => 'auth.jwt'], function () {
 Route::get('logout', 'ApiController@logout');
 Route::get('user', 'ApiController@getAuthUser');
 Route::get('products', 'ProductController@index');
 Route::get('products/{id}', 'ProductController@show');
 Route::post('products', 'ProductController@store');
 Route::put('products/{id}', 'ProductController@update');
 Route::delete('products/{id}', 'ProductController@destroy');
});

  

更新 User 模型

JWT 須要在 User 模型中實現 Tymon\JWTAuth\Contracts\JWTSubject 接口。 此接口須要實現兩個方法 getJWTIdentifier 和 getJWTCustomClaims。使用如下內容更新 app/User.php 。

<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
 use Notifiable;
 /**
 * The attributes that are mass assignable.
 *
 * @var array
 */
 protected $fillable = [
 'name', 'email', 'password',
 ];
 /**
 * 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 [];
 }
}

  

JWT 身份驗證邏輯

讓咱們使用 JWT 身份驗證在 laravel 中寫 Restful API 的邏輯。

用戶註冊時須要姓名,郵箱和密碼。那麼,讓咱們建立一個表單請求來驗證數據。經過運行如下命令建立名爲 RegisterAuthRequest 的表單請求:

php artisan make:request RegisterAuthRequest

  

它將在 app/Http/Requests 目錄下建立 RegisterAuthRequest.php 文件。將下面的代碼黏貼至該文件中。

<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterAuthRequest extends FormRequest
{
 /**
 * 肯定是否受權用戶發出此請求
 *
 * @return bool
 */
 public function authorize()
 {
 return true;
 }
 /**
 * 獲取應用於請求的驗證規則
 *
 * @return array
 */
 public function rules()
 {
 return [
 'name' => 'required|string',
 'email' => 'required|email|unique:users',
 'password' => 'required|string|min:6|max:10'
 ];
 }
}

  

運行如下命令建立一個新的 ApiController :

php artisan make:controller ApiController

  

這將會在 app/Http/Controllers 目錄下建立 ApiController.php 文件。將下面的代碼黏貼至該文件中。

<?php
namespace App\Http\Controllers;
use App\Http\Requests\RegisterAuthRequest;
use App\User;
use Illuminate\Http\Request;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class ApiController extends Controller
{
 public $loginAfterSignUp = true;
 public function register(RegisterAuthRequest $request)
 {
 $user = new User();
 $user->name = $request->name;
 $user->email = $request->email;
 $user->password = bcrypt($request->password);
 $user->save();
 if ($this->loginAfterSignUp) {
 return $this->login($request);
 }
 return response()->json([
 'success' => true,
 'data' => $user
 ], 200);
 }
 public function login(Request $request)
 {
 $input = $request->only('email', 'password');
 $jwt_token = null;
 if (!$jwt_token = JWTAuth::attempt($input)) {
 return response()->json([
 'success' => false,
 'message' => 'Invalid Email or Password',
 ], 401);
 }
 return response()->json([
 'success' => true,
 'token' => $jwt_token,
 ]);
 }
 public function logout(Request $request)
 {
 $this->validate($request, [
 'token' => 'required'
 ]);
 try {
 JWTAuth::invalidate($request->token);
 return response()->json([
 'success' => true,
 'message' => 'User logged out successfully'
 ]);
 } catch (JWTException $exception) {
 return response()->json([
 'success' => false,
 'message' => 'Sorry, the user cannot be logged out'
 ], 500);
 }
 }
 public function getAuthUser(Request $request)
 {
 $this->validate($request, [
 'token' => 'required'
 ]);
 $user = JWTAuth::authenticate($request->token);
 return response()->json(['user' => $user]);
 }
}

  

讓我解釋下上面的代碼發生了什麼。

在 register 方法中,咱們接收了 RegisterAuthRequest 。使用請求中的數據建立用戶。若是 loginAfterSignUp 屬性爲 true ,則註冊後經過調用 login 方法爲用戶登陸。不然,成功的響應則將伴隨用戶數據一塊兒返回。

在 login 方法中,咱們獲得了請求的子集,其中只包含電子郵件和密碼。以輸入的值做爲參數調用 JWTAuth::attempt() ,響應保存在一個變量中。若是從 attempt 方法中返回 false ,則返回一個失敗響應。不然,將返回一個成功的響應。

在 logout 方法中,驗證請求是否包含令牌驗證。經過調用 invalidate 方法使令牌無效,並返回一個成功的響應。若是捕獲到 JWTException 異常,則返回一個失敗的響應。

在 getAuthUser 方法中,驗證請求是否包含令牌字段。而後調用 authenticate 方法,該方法返回通過身份驗證的用戶。最後,返回帶有用戶的響應。

身份驗證部分如今已經完成。

構建產品部分

要建立產品部分,咱們須要 Product 模型,控制器和遷移文件。運行如下命令來建立 Product 模型,控制器和遷移文件。

php artisan make:model Product -mc

  

它會在 database/migrations 目錄下建立一個新的數據庫遷移文件 create_products_table.php,更改 up 方法。
public function up()
{
 Schema::create('products', function (Blueprint $table) {
 $table->increments('id');
 $table->integer('user_id');
 $table->string('name');
 $table->integer('price');
 $table->integer('quantity');
 $table->timestamps();
 $table->foreign('user_id')
 ->references('id')
 ->on('users')
 ->onDelete('cascade');
 });
}

  

向 Product 模型中添加 fillable 屬性。在 app 目錄下打開 Product.php 文件並添加屬性。

protected $fillable = [
 'name', 'price', 'quantity'
];

  

如今在 .env 文件中設置數據庫憑證,並經過運行如下命令遷移數據庫。

php artisan migrate

  

如今,咱們必須在 User 模型中添加一個關係來檢索相關產品。在 app/User.php 中添加如下方法。

public function products()
{
 return $this->hasMany(Product::class);
} 

  

在 app/Http/Controllers 目錄下打開 ProductController.php 文件。在文件開頭添加 use 指令覆蓋上一個。

use App\Product;
use Illuminate\Http\Request;
use JWTAuth;

  

如今咱們將實現五個方法。

index, 爲通過身份認證的用戶獲取全部產品列表

show, 根據 ID 獲取特定的產品

store, 將新產品存儲到產品列表中

update, 根據 ID 更新產品詳情

destroy, 根據 ID 從列表中刪除產品

添加一個構造函數來獲取通過身份認證的用戶,並將其保存在 user 屬性中。

protected $user;
public function __construct()
{
 $this->user = JWTAuth::parseToken()->authenticate();
}

  

parseToken 將解析來自請求的令牌, authenticate 經過令牌對用戶進行身份驗證。

讓咱們添加 index 方法。

public function index()
{
 return $this->user
 ->products()
 ->get(['name', 'price', 'quantity'])
 ->toArray();
}

  

上面的代碼很是簡單,咱們只是使用 Eloquent 的方法獲取全部的產品,而後將結果組成一個數組。最後,咱們返回這個數組。Laravel 將自動將其轉換爲 JSON ,並建立一個爲 200 成功的響應碼。

繼續實現 show 方法。

public function show($id)
{
 $product = $this->user->products()->find($id);
 if (!$product) {
 return response()->json([
 'success' => false,
 'message' => 'Sorry, product with id ' . $id . ' cannot be found'
 ], 400);
 }
 return $product;
}

  

這個也很是容易理解。咱們只須要根據 ID 找到該產品。若是產品不存在,則返回 400 故障響應。不然,將返回產品數組。

接下來是 store 方法

public function store(Request $request)
{
 $this->validate($request, [
 'name' => 'required',
 'price' => 'required|integer',
 'quantity' => 'required|integer'
 ]);
 $product = new Product();
 $product->name = $request->name;
 $product->price = $request->price;
 $product->quantity = $request->quantity;
 if ($this->user->products()->save($product))
 return response()->json([
 'success' => true,
 'product' => $product
 ]);
 else
 return response()->json([
 'success' => false,
 'message' => 'Sorry, product could not be added'
 ], 500);
}

  

在 store 方法中,驗證請求中是否包含名稱,價格和數量。而後,使用請求中的數據去建立一個新的產品模型。若是,產品成功的寫入數據庫,會返回成功響應,不然返回自定義的 500 失敗響應。

實現 update 方法

public function update(Request $request, $id)
{
 $product = $this->user->products()->find($id);
 if (!$product) {
 return response()->json([
 'success' => false,
 'message' => 'Sorry, product with id ' . $id . ' cannot be found'
 ], 400);
 }
 $updated = $product->fill($request->all())
 ->save();
 if ($updated) {
 return response()->json([
 'success' => true
 ]);
 } else {
 return response()->json([
 'success' => false,
 'message' => 'Sorry, product could not be updated'
 ], 500);
 }
}

  

在 update 方法中,咱們經過 id 取得產品。若是產品不存在,返回一個 400 響應。而後,咱們把請求中的數據使用 fill 方法填充到產品詳情。更新產品模型並保存到數據庫,若是記錄成功更新,返回一個 200 成功響應,不然返回 500 內部服務器錯誤響應給客戶端。

如今,讓咱們實現 destroy 方法。

public function destroy($id)
{
 $product = $this->user->products()->find($id);
 if (!$product) {
 return response()->json([
 'success' => false,
 'message' => 'Sorry, product with id ' . $id . ' cannot be found'
 ], 400);
 }
 if ($product->delete()) {
 return response()->json([
 'success' => true
 ]);
 } else {
 return response()->json([
 'success' => false,
 'message' => 'Product could not be deleted'
 ], 500);
 }
}

  

在 destroy 方法中,咱們根據 ID 獲取產品,若是產品不存在,則返回 400 響應。而後咱們刪除產品後並根據刪除操做的成功狀態返回適當的響應。

控制器代碼如今已經完成,完整的控制器代碼在這。

測試

咱們首先來測試身份認證。咱們將使用 serve 命令在開發機上啓動 Web 服務,你也可使用虛擬主機代替。運行如下命令啓動 Web 服務。

php artisan serve

  

它將監聽 localhost:8000

爲了測試 restful API's,咱們使用 Postman。填寫好請求體以後,咱們請求一下 register 路由。

發送請求,你將得到令牌。

咱們的用戶現已註冊並經過身份驗證。咱們能夠發送另外一個請求來檢測 login 路由,結果會返回 200 和令牌。

 

獲取用戶詳情

 

測試身份認證已完成。接下來測試產品部分,首先建立一個產品。

如今,經過請求 index 方法獲取產品。

你能夠測試其它路由,它們都將正常工做。

相關文章
相關標籤/搜索