使用Laravel Passport用PHP构建一个安全的API

911 阅读7分钟

当建立一个移动应用的后端或使用现代JavaScript框架时,你不能避免RESTful API。需要注意的是,当使用API进行通信时,会话状态在请求之间不被维护。因此,你需要使用一个令牌来验证和授权用户。

Laravel有一个预先建立的机制,以确保适当的安全,使得它很容易建立这样一个资源。本教程将告诉你如何使用Laravel Passport来建立一个安全的Laravel后端API。当你到达终点时, 你将学习如何创建一个安全的Laravel API以及如何加强你现有的API的安全性.

要求

要遵循本教程,对用Laravel构建应用程序有一个基本的了解是有帮助的。你还需要全局安装Composer来管理依赖关系,以及Postman来测试端点。

建造什么

通过建立一个API,创建一个大型科技公司的CEO名单,学习如何建立一个安全的Laravel API。申请书将列出每位首席执行官的以下信息

  • 命名
  • 他们被任命为首席执行官的年份
  • 公司总部所在地
  • 公司的业务性质

为了确保这个应用程序的安全,Laravel Passport被安装,并为每个认证的用户生成一个访问令牌。收到访问令牌的用户将能够访问安全端点。

开始工作

首先, 使用ComposerLaravel安装程序在你的电脑上为你的新Laravel应用程序创建框架, 并按照Laravel官方网站上的指示来设置Laravel安装程序.一旦设置完成,运行以下命令

$ laravel new laravel-backend-api

如果你使用Composer来安装同一个应用程序,请运行以下命令

$ composer create-project --prefer-dist laravel/laravel laravel-backend-api

这个命令将在你安装Laravel及其依赖的开发文件夹中创建一个名为_laravel-backend-api_的新文件夹, 根据你指定的参数.导航到这个新文件夹。

导航到这个新文件夹,使用Laravel Artisan命令运行应用程序,如下所示

// move into the project
$ cd laravel-backend-api

// run the application
$ php artisan serve

从你的
浏览器,进入http://localhost:8000にアクセスすると,你应该看到一个类似以下的页面Laravel ホームページ

创建并连接到数据库

Laravel现在已经安装并运行。下一步是建立一个与数据库的连接。首先,确保数据库已经创建,并更新_.env_文件中的以下变量值。

  • DB_DATABASE
  • DB_USERNAME
  • DB_PASSWORD

这就完成了数据库的设置, 但在我们建立API之前, 我们需要安装和配置Laravel Passport.

安装和配置Laravel Passport

Laravel Passport实现了0Auth2服务器为Laravel应用程序所要求的所有功能. laravel Passport允许你轻松地生成一个个人访问令牌,并唯一地识别当前认证的用户。然后,生成的令牌可用于验证所有用户。通过在每个请求中添加生成的令牌,每个用户都可以访问受保护的路线。首先,按Ctrl+C停止应用程序的运行。接下来, 使用Composer来安装Laravel Passport.执行以下命令

$ composer require laravel/passport

一旦安装完成,将生成一个新的迁移文件,其中包含存储客户端和访问令牌信息所需的表格。这个文件将被应用程序使用。执行下面的命令来迁移数据库。

$ php artisan migrate

接下来,运行以下命令来创建生成安全访问令牌所需的加密密钥。

$ php artisan passport:install

使用上述命令完成安装过程后,将Laravel\Passport\HasApiTokens特质添加到_App/User_模型中,如下图所示。

// app/User.php

<?php

namespace App;

...
use Laravel\Passport\HasApiTokens; // include this

class User extends Authenticatable
{
    use Notifiable, HasApiTokens; // update this line

    ...
}

添加这个特性将允许模型使用一个辅助方法来检查认证用户的令牌和范围。

为了注册发行和撤销个人访问令牌和客户访问令牌所需的路由,可以在AuthServiceProvider的内部调用Passport::routes方法。在boot方法中调用它。打开_app/Providers/AuthServiceProvider_文件并更新内容,如下所示

// app/Providers/AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport; // add this 

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
         'App\Model' => 'App\Policies\ModelPolicy', // uncomment this line
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes(); // Add this
    }
}

一旦你注册了Passport::routes(),你几乎已经准备好使用Laravel Passport来处理你应用程序中的所有认证和授权过程。

最后,我们需要确保我们的应用程序能够使用Passport的TokenGuard,对任何传入的API请求进行认证。打开_config/auth_配置文件,添加以下内容api将认证防护的driver选项设置为passport

// config/auth

<?php

return [
    ...

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'passport', // set this to passport
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    ...
];

为公司信息创建一个迁移文件

当你安装新的Laravel时,_用户_模型和迁移文件会自动生成.这对保持数据库的标准结构很有用:打开_app/User.php_文件,确保它看起来像这样

// app/User.php

<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use Notifiable, HasApiTokens;

    /**
     * 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',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

对于_数据库/migrations/***_create_users_table.php_中的用户迁移文件,请确保它看起来像这样

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

上述文件中指定的字段足以为你正在创建的应用程序验证用户。上述文件中指定的字段足以为你在这里创建的应用程序验证用户身份,所以没有必要改变它们。

接下来,我们将使用artisan命令将模型实例化,并为_CEO_表生成一个数据库迁移文件。执行以下命令

$ php artisan make:model CEO -m

上述命令将在_app_目录下创建模型,并在_数据库/migrations_文件夹下创建一个新的迁移文件。 -m 选项是--migration的缩写。 它指示artisan命令为模型创建一个迁移文件。

接下来,打开新创建的迁移文件,按如下步骤更新其内容。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCEOSTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('c_e_o_s', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('company_name');
            $table->year('year');
            $table->string('company_headquarters');
            $table->string('what_company_does');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('c_e_o_s');
    }
}

我们已经添加了字段name,company_name,year,company_headquarterswhat_company_does字段已被添加。

打开_app/CEO.php_文件,更新内容如下

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class CEO extends Model
{
    protected $fillable = [
        'name', 'company_name', 'year', 'company_headquarters', 'what_company_does'
    ];
}

默认情况下,Eloquent模型受到保护,不会被批量分配。因此,我们已经指定了要批量分配的属性。

再次,运行下面的迁移命令,用新创建的表和字段更新数据库。

$ php artisan migrate

在数据库被更新后,为应用程序创建一个控制器。我们还将为注册、登录和创建CEO的详细信息创建端点。

创建控制器

控制器接受传入的HTTP请求,通过将它们重定向到适当的行动或方法来处理它们,并返回适当的响应。由于我们在这里建立了一个API,大多数响应将是JSON格式;JSON是RESTful APIs使用的标准格式。

认证控制器

首先,我们使用artisan命令来为我们的应用程序生成一个认证控制器。这个控制器将处理用户注册和登录应用程序的请求。执行以下命令

$ php artisan make:controller API/AuthController

这个命令将在_app/Http/Controllers_中创建一个新的API文件夹,并在该文件夹中创建一个名为_AuthController.php_的新文件。打开新创建的控制器文件,更新内容如下

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $validatedData = $request->validate([
            'name' => 'required|max:55',
            'email' => 'email|required|unique:users',
            'password' => 'required|confirmed'
        ]);

        $validatedData['password'] = bcrypt($request->password);

        $user = User::create($validatedData);

        $accessToken = $user->createToken('authToken')->accessToken;

        return response([ 'user' => $user, 'access_token' => $accessToken]);
    }

    public function login(Request $request)
    {
        $loginData = $request->validate([
            'email' => 'email|required',
            'password' => 'required'
        ]);

        if (!auth()->attempt($loginData)) {
            return response(['message' => 'Invalid Credentials']);
        }

        $accessToken = auth()->user()->createToken('authToken')->accessToken;

        return response(['user' => auth()->user(), 'access_token' => $accessToken]);

    }
}

上述register方法将为你的应用程序的用户处理注册过程。它使用Laravel的认证方法来验证注册所需的所有字段都已填写。使用此验证器name,email,password,password_confirmation为必填字段。验证器检查该值是否为用户指定,并返回适当的反馈。

最后,login方法用于验证是否输入了适当的凭证,用户得到了验证。认证成功后,会生成accessToken,它能唯一地识别登录的用户,并发送一个JSON响应。为了向受保护的安全路由发送后续的HTTP请求并成功处理这些请求,必须将生成的accessToken作为认证头传递。如果没有传递这个令牌,将返回一个响应,表明该请求没有得到验证。

创建一个CEO控制器

使用同样的artisan命令,自动创建一个新的控制器。在这种情况下,我们将创建一个API资源控制器,在Laravel中,它是一个控制器,处理所有特定模型的HTTP请求。在这种情况下,我们将创建一个控制器,处理_CEO_模型的所有创建、读取、更新和删除请求。执行以下命令

$ php artisan make:controller API/CEOController --api --model=CEO

执行上述命令后,一个API资源控制器将被创建。由于我们在这里只建立了一个API,所以不包括创建和编辑视图。进入app/Http/Controllers/API/CEOController.php,更新内容如下

<?php

namespace App\Http\Controllers\API;

use App\CEO;
use App\Http\Controllers\Controller;
use App\Http\Resources\CEOResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class CEOController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $ceos = CEO::all();
        return response([ 'ceos' => CEOResource::collection($ceos), 'message' => 'Retrieved successfully'], 200);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $data = $request->all();

        $validator = Validator::make($data, [
            'name' => 'required|max:255',
            'year' => 'required|max:255',
            'company_headquarters' => 'required|max:255',
            'what_company_does' => 'required'
        ]);

        if($validator->fails()){
            return response(['error' => $validator->errors(), 'Validation Error']);
        }

        $ceo = CEO::create($data);

        return response([ 'ceo' => new CEOResource($ceo), 'message' => 'Created successfully'], 200);
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\CEO  $ceo
     * @return \Illuminate\Http\Response
     */
    public function show(CEO $ceo)
    {
        return response([ 'ceo' => new CEOResource($ceo), 'message' => 'Retrieved successfully'], 200);

    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\CEO  $ceo
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, CEO $ceo)
    {

        $ceo->update($request->all());

        return response([ 'ceo' => new CEOResource($ceo), 'message' => 'Retrieved successfully'], 200);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param \App\CEO $ceo
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function destroy(CEO $ceo)
    {
        $ceo->delete();

        return response(['message' => 'Deleted']);
    }
}

在上面的代码片段中,已经创建了五个不同的方法,每个方法都有内置的逻辑来执行一个特定的功能。以下是每种方法的功能概述

  • index: 这个方法从数据库中检索所有CEO的列表,并以JSON结构的资源集合形式返回,更多关于Laravel资源集合的信息将在下一节给出。
  • store :该方法接收一个HTTP请求的实例,并通过注入一个依赖关系创建一个新的CEO细节。所有发送的数值都是用$request来检索的。我们还检查请求中的所有强制字段是否有指定的值。最后,新创建的CEO的详细信息将作为JSON响应返回。
  • show :该方法唯一地识别了一个特定的首席执行官,并将他们的细节作为一个JSON响应返回。
  • update :该方法将HTTP请求和需要编辑的特定项目作为参数。它将对传递的模型实例进行更新,并返回适当的响应。
  • destroy :该方法接收一个需要删除的特定项目的实例,并将其从数据库中删除。

一些方法,如show(),update()destroy(),需要一个特定的模型ID来唯一地识别一个特定的项目。一些方法,如 ,和需要一个特定的模型ID来唯一地识别一个特定的项目。注意这里,你可以直接注入一个模型的实例。这是通过使用Laravel的隐式根模型绑定来实现的。

使用这个方法,CEO的一个实例将被自动注入方法中。如果没有找到,将返回一个状态代码404。这简化了过程,允许你直接使用模型的实例,而不必运行查询来获得与其ID对应的模型。

创建一个资源

Laravel Eloquent资源可以用来将模型和集合转换成JSON格式。该资源在数据库和控制器之间充当数据转换层。这提供了一个统一的接口,可以在你的应用程序中的任何地方使用。使用以下命令在_CEO_模型中创建一个资源。

$ php artisan make:resource CEOResource

这个命令将在_app/Http/Resources_目录下创建一个名为_CEOResource.php_的新资源文件。打开新创建的文件。内容如下

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class CEOResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return parent::toArray($request);
    }
}

toArray()方法中,parent::toArray($request)在发送JSON响应时自动转换所有发布的模型属性。

更多关于使用Eloquent资源的好处,请看Laravel官方文档中的Eloquent: API资源

更新路线文件

为了完成控制器中创建的方法的端点配置,更新_routes.api.php_文件的内容,如下所示

Route::post('/register', 'Api\AuthController@register');
Route::post('/login', 'Api\AuthController@login');

Route::apiResource('/ceo', 'Api\CEOController')->middleware('auth:api');

要查看你在这个应用程序中创建的所有路由的列表,请在终端运行以下命令。

$ php artisan route:list

你会看到类似于以下的内容

アウトプット

这将使你能够看到你的应用程序中的所有路线。

_routes/api.php_文件中的所有路由都以/api/为前缀,因此如果你想向你刚刚创建的端点发送HTTP请求,你需要添加这个前缀或有很多人。

运行应用程序

接下来,我们将用下面的命令运行该应用程序,以测试我们到目前为止所实现的所有逻辑。

$ php artisan serve

注册一个用户

要注册一个用户,请向端点http://localhost:8000/api/register发送一个POST HTTP请求,并输入适当的细节,如下所示。

詳細の入力

具体细节可能有所不同,但你可以按以下方式指定请求的键和值

KEY                                       VALUE
name                                     John Doe
email                                    john@me.com
password                                  password
password_confirmation          password

登录

成功完成注册后,你可以进入http://localhost:8000/api/login,输入你的详细资料并接受认证。

ログイン

如果你使用了上一节中指定的键和值,你的请求应该是这样的

KEY                                       VALUE
email                                    john@me.com
password                                  password

添加一个承载令牌

登录后,从响应中复制access_token的值,并点击授权标签。从下拉菜单中选择承载令牌,并粘贴你刚刚复制的access_token的值。

ベアラートークン追加

创造一个CEO

通过指定如下所示的细节,创建一个新的CEO。

CEO作成

检索CEO名单

要检索你刚刚创建的CEO名单,请向端点GETHTTP请求,如下图所示http://localhost:8000/api/ceo

CEOリスト取得

展示CEO们的风采

向URL http://localhost:8000/api/ceo/1发送一个GET HTTP请求,以显示特定CEO的详细信息。

CEO表示

请注意,这里1,作为一个端点,表示id,以便从数据库中检索到一个特定的记录。 你可以使用id来显示任何记录的信息。

编辑一个CEO

CEOリスト編集

删除一个CEO

CEO削除

摘要

在这个教程中,你已经学会了如何使用Laravel Passport来建立一个安全的RESTful API。本教程中创建的例子涵盖了许多应用程序需要的基本CRUD(创建、读取、更新和删除)过程。

我们希望你能在这里学到的东西的基础上,将其应用于你现有的或新的项目。