在使用跨平台应用程序时, API 是一个非常不错的选择。 除了网站,您的产品可能还有 Android 和 iOS 应用程序。 在这种情况下, API 也是同样出色的,因为您可以在不更改任何后端代码的情况下编写不同的前端。 使用 API 时,只需使用一些参数点击 GET , POST 或其他类型的请求,服务器就会返回 JSON (JavaScript Object Notation) 格式的一些数据,这些数据由客户端应用程序处理。
说明
我们先写下我们的应用程序详细信息和功能。 我们将使用 JWT 身份验证在 laravel 中使用 restful API 构建基本用户产品列表。
A User 将会使用以下功能
- 注册并创建一个新帐户
- 登录到他们的帐户
- 注销和丢弃 token 并离开应用程序
- 获取登录用户的详细信息
- 检索可供用户使用的产品列表
- 按 ID 查找特定产品
- 将新产品添加到用户产品列表中
- 编辑现有产品详细信息
- 从用户列表中删除现有产品
A User 必填
- name
- password
A Product 必填
- name
- price
- quantity
创建新的项目
通过运行下面的命令,我们就可以开始并创建新的 Laravel 项目。
composer create-project --prefer-dist laravel/laravel jwt
这会在名为 jwt 的目录下创建一个新的 Laravel 项目。
配置 JWT 扩展包
我们会使用 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','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;
现在我们将实现五个方法。
show,根据 ID 获取特定的产品
store,将新产品存储到产品列表中
update,根据 ID 更新产品详情
destroy,根据 ID 从列表中删除产品
添加一个构造函数来获取经过身份认证的用户,并将其保存在 user 属性中。
protected $user; public function __construct() { $this->user = JWTAuth::parseToken()->authenticate(); }
parseToken 将解析来自请求的令牌, authenticate 通过令牌对用户进行身份验证。
public function index() { return $this->user ->products() ->get(['name','quantity']) ->toArray(); }
上面的代码非常简单,我们只是使用 Eloquent 的方法获取所有的产品,然后将结果组成一个数组。最后,我们返回这个数组。Laravel 将自动将其转换为 JSON ,并创建一个为 200 成功的响应码。
继续实现 show 方法。
public function show($id) { $product = $this->user->products()->find($id); if (!$product) { return response()->json([ 'success' => false,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,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,400); } $updated = $product->fill($request->all()) ->save(); if ($updated) { return response()->json([ 'success' => true ]); } else { return response()->json([ 'success' => false,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,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 路由。
@H_657_404@
发送请求,你将获得令牌。
@H_657_404@
我们的用户现已注册并通过身份验证。我们可以发送另一个请求来检测 login 路由,结果会返回 200 和令牌。
@H_657_404@
@H_657_404@
测试身份认证已完成。接下来测试产品部分,首先创建一个产品。
@H_657_404@
@H_657_404@
你可以测试其它路由,它们都将正常工作。