如何使用 Laravel Sanctum 限制 SPA 网络应用程序的设备登录

How to limit device login for SPA web app using Laravel Sanctum

我正在使用 Laravel sanctum 构建 REST API 身份验证,我想让用户可以在多个设备上登录并且它受限于 2 个设备,假设用户 A 和用户 B 是当用户C登录时登录,用户A注销等。如何实现,什么概念?

通常我会在电子邮件和密码正确时登录 api 然后 return 令牌。

我是从 netflix 那里了解到的,它的设备有限,无法观看电影。

您可以简单地从您的 personal_access_tokens table 中检查您向该用户发放了多少令牌,如下所示:

因此,当您在为用户签发新令牌之前登录用户时,只需 运行 这样的查询:

$issuedTokens = PersonalAccessToken::where('tokenable_type', User::class)
                                   ->where('tokenable_id', $userId)
                                   ->get();
if ($issuedTokens->count() > 1) {
    $returnMessage = 'You have to remove on of the following devices:';
    $deviceNames = $issuedTokens->pluck('name')->toArray();
}
// Things are fine, proceed

如果您想进一步增强功能,您可能希望通过添加进入者的移动详细信息以及他们的 country/city 访问权限来扩展 PersonalAccessToken 模型。

要扩展它,请添加迁移和模型文件,如下所示:

<?php

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

// Remember to change this line, if you wish, back to the old way.
return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('personal_access_tokens', function (Blueprint $table) {
            $table->id();
            $table->morphs('tokenable');
            $table->string('name');
            $table->string('token', 64)->unique();
            $table->string('country_name')->nullable();
            $table->text('abilities')->nullable();
            $table->json('mobile_app_details')->nullable();
            $table->timestamp('last_used_at')->nullable();
            $table->timestamps();
        });
    }

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

你的模特:

<?php


namespace App\Models;

use Eloquent;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Laravel\Sanctum\PersonalAccessToken as PersonalAccessTokenAlias;

/**
 * App\Models\PersonalAccessToken
 *
 * @property int $id
 * @property string $tokenable_type
 * @property int $tokenable_id
 * @property string $name
 * @property string $token
 * @property array|null $abilities
 * @property object|null $mobile_app_details
 * @property string|null $country_name
 * @property Carbon|null $last_used_at
 * @property Carbon|null $created_at
 * @property Carbon|null $updated_at
 * @property-read Model|\Eloquent $tokenable
 * @method static Builder|PersonalAccessToken newModelQuery()
 * @method static Builder|PersonalAccessToken newQuery()
 * @method static Builder|PersonalAccessToken query()
 * @method static Builder|PersonalAccessToken whereAbilities($value)
 * @method static Builder|PersonalAccessToken whereCreatedAt($value)
 * @method static Builder|PersonalAccessToken whereId($value)
 * @method static Builder|PersonalAccessToken whereLastUsedAt($value)
 * @method static Builder|PersonalAccessToken whereMobileAppDetails($value)
 * @method static Builder|PersonalAccessToken whereName($value)
 * @method static Builder|PersonalAccessToken whereToken($value)
 * @method static Builder|PersonalAccessToken whereTokenableId($value)
 * @method static Builder|PersonalAccessToken whereTokenableType($value)
 * @method static Builder|PersonalAccessToken whereUpdatedAt($value)
 * @mixin Eloquent
 * @noinspection PhpFullyQualifiedNameUsageInspection
 * @noinspection PhpUnnecessaryFullyQualifiedNameInspection
 */
class PersonalAccessToken extends PersonalAccessTokenAlias
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'token',
        'abilities',
        'mobile_app_details',
        'country_name',
    ];

    protected $casts = [
        'abilities' => 'json',
        'last_used_at' => 'datetime',
        'mobile_app_details' => 'object'
    ];

}

最后一个重要步骤是告诉 Laravel 忽略原始迁移并加载自定义模型,因此在您的 AppServiceProvider:

<?php

namespace App\Providers;

use App\Models\PersonalAccessToken;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\Sanctum;


class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        Sanctum::ignoreMigrations();
        // other lines go here
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
        // other lines go here
    }
}