如何使用 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
}
}
我正在使用 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
}
}