Skip to content

在 Laravel 中使用 JSON Web Tokens 创建 API

在 API 中使用 JWT 进行用户身份验证特别有用,因为它不需要在服务器上实现会话,而且 JWT 本身包含与发出请求的用户相对应的信息,可用于验证其真实性。

了解有关 JWT 的更多信息

首先,我们需要安装 Laravel 并创建一个项目。安装 Laravel 5.8 的说明。安装 Laravel 后,我们继续创建一个项目。

bash
$ laravel new webpage

要在 Laravel 中实现 JWT,我们将使用名为 tymondesigns/jwt-auth 的模块,使用以下代码:

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

然后要生成配置文件,我们运行以下代码:

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

我们继续为项目生成密钥:

bash
$ php artisan jwt:secret

为了让 Laravel 自动连接到我们的数据库并将其用作用户验证方法,我们必须创建一个具有以下列的 users 表:

  • id PRIMARY KEY AUTOINCREMENT
  • name VARCHAR
  • surname VARCHAR
  • email VARCHAR
  • password VARCHAR
  • create_at TIMESTAMP
  • updated_at TIMESTAMP

默认情况下,User 模型将使用 users 表。如果要指定表的其他名称,可以在 app\User.php 中使用以下代码定义要使用的名称:

php
...
protected $table = ''; // 我们放入表名 
...

以下步骤在代码级别。我们需要在 User 模型中实现 Tymon\JWTAuth\Contracts\JWTSubject。在这个模型中,我们必须实现两个方法:getJWTIdentifier()getJWTCustomClaims()

app\User.php 中的 User 模型应类似于:

php
<?php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;  
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject {
    use Notifiable;
    
    protected $fillable = [
        'name', 'surname', 'email', 'password',  
    ];
    
    protected $hidden = [
        'password','remember_token', 
    ];
    
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    
    public function getJWTIdentifier() { 
        return $this->getKey();
    }
    
    public function getJWTCustomClaims() {
        return [];
    }  
}

现在为了让 Laravel 使用 JWT 作为身份验证方法,我们打开 config/auth.php 文件并修改以下数据:

php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],  
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

现在我们继续添加身份验证控制器。我们将其称为 AuthController。我们可以手动创建它,或使用以下命令:

bash
$ php artisan make:controller AuthController

在这个控制器中,我们放入以下代码:

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 AuthController extends Controller {
    public $loginAfterSignUp = true;
    
    public function register(Request $request) {
        $user = new User();
        $user->name = $request->name;
        $user->surname = $request->surname;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();
    
        if ($this->loginAfterSignUp) {
            return $this->login($request);
        }
    
        return response()->json([
            'status' => 'ok',
            '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([
                'status' => 'invalid_credentials',
                'message' => 'Incorrect email or password.',
            ], 401);
        }
    
        return response()->json([
            'status' => 'ok',
            'token' => $jwt_token,
        ]);
    }
    
    public function logout(Request $request) {
        $this->validate($request, [
            'token' => 'required'
        ]); 
    
       try {
           JWTAuth::invalidate($request->token);
           return response()->json([
               'status' => 'ok',
               'message' => 'Logout successful.'
           ]);
       } catch (JWTException $exception) {
           return response()->json([
               'status' => 'unknown_error',
               'message' => 'The user could not be logged out.'  
           ], 500);
       }
    }
    
    public function getAuthUser(Request $request) {
        $this->validate($request, [
            'token' => 'required'
        ]);
    
        $user = JWTAuth::authenticate($request->token);
        return response()->json(['user' => $user]);
    }
}

现在我们在 routes\api.php 中创建将访问这些方法的路由:

php
<?php
use Illuminate\Http\Request;

// 这些路由可以在不提供有效令牌的情况下访问。  
Route::post('/login','AuthController@login');
Route::post('/register','AuthController@register');
// 这些路由需要一个有效的令牌才能被访问。
Route::group(['middleware' => 'auth.jwt'], function () {
     Route::post('/logout', 'AuthController@logout');  
});

此外,我们可以在 public\index.php 的开头添加以下代码,以避免在测试期间发生 CORS 错误:

php
// 允许来自任何来源的请求
header('Access-Control-Allow-Origin: *');
// 允许使用 GET、PUT、POST、DELETE 和 OPTIONS 方法的请求 
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
// 允许 Content-Type 和 Authorization 标头  
header('Access-Control-Allow-Headers: Content-Type, Authorization');

这就结束了使用 JSON Web Tokens 的 Laravel 配置部分。现在我们继续使用 Insomnia 进行一些测试请求,以验证我们的路由是否正常工作。

我们向 /api/register 路由发出请求,并确认我们收到一个 status: ok 和一个 token

JWT registration test

这意味着该用户已在数据库中注册,并且自动生成了一个令牌,我们必须在每个受保护路由请求中发送该令牌。

我们还可以测试 /api/login

JWT login test

令牌可以通过不同的方式在每个请求中发送:

  • 我们可以在使用 GET 的情况下通过 url 发送,例如 .../api/products?token=eyJ0eXAiOiJ...
  • 作为在 POST 中发送的 JSON 的一个属性。
  • 或者作为 Authorization: Bearer eyJ0eXAiOiJ... 标头的一部分。

在下面的示例中,我们将其作为 JSON 中的一个属性发送,以使用 api/logout 注销用户。

JWT logout test

以同样的方式,我们可以继续创建更多需要有效令牌才能访问的受保护路由。像往常一样,任何问题都可以留在评论中。祝好。