使用 Sanctum 模拟用户
Impersonate users with Sanctum
我正在使用 Sanctum 为 Laravel 实施一个简单的模拟系统。早些时候我使用 Tymondesigns/jwt-auth with Rickycezar/laravel-jwt-impersonate,但我们最近放弃了 Tymon JWT 转而使用 Sanctum。
我没有用 Sanctum 实现 laravel-jwt-impersonate,或者它是从中分叉出来的原始 404labfr/laravel-impersonate。所以...我决定自己尝试实现一个非常简单的模拟系统。
这就是我现在要做的事情:
当管理员调用 impersonate()
函数时,我为被模拟的用户创建了一个令牌。此令牌 returned 到前端并用作不记名令牌。它似乎运行良好,在执行此操作后,应用程序就像我是模拟用户一样(因为我实际上是作为该用户“登录”的)。
public function impersonate($user)
{
$user = User::find($user);
return $user->createToken('IMPERSONATE token');
}
下一步是我想知道的。如何结束模拟。如果我注销,创建的模拟令牌将被删除,所以一切都很好......但这意味着管理员现在已注销并且必须重新登录。
我想使用之前的令牌重新登录管理员。但我不知道如何 return 管理员登录时使用的令牌。
所以我的问题:
- 你会如何解决这个问题?
- 您看到哪些明显的安全隐患(只能从管理员帐户进行模拟,并且只能模拟非管理员帐户)?
你就快完成了!我们有一个应用程序也使用 Sanctum,但使用会话而不是令牌。在我们模拟用户之前,我们将 admin/actual 用户的 ID 放入会话中:
$request->session()->put('impersonate', true); // if you need to check if session is impersonated or not
$request->session()->put('impersonate_admin_id', Auth::id());
Auth::login($user); // then impersonate
因此我们可以使用用户 ID:
在 logout/exit 模拟中重新登录到管理员
Auth::loginUsingId($request->session()->get('impersonate_admin_id'));
尽管我的示例是基于会话的,但您明白了要点。由于您的是基于令牌的,并且您不能将其存储在会话或 cookie 中,因此我建议使用 DB 或 Redis/Cache.
这是我寻求的解决方案。结果比最初预期的要大一点。它似乎与我们将 Tymondesigns/jwt-auth 与 Rickycezar/laravel-jwt-impersonate 一起使用时一样有效。我对响应使用相同的结构,因此前端不需要进行任何更改。
我还在 this blog post 中概述了更多内容。
迁移
public function up()
{
Schema::create('impersonations', function (Blueprint $table) {
$table->id();
$table->bigInteger('personal_access_token_id')->unsigned();
$table->bigInteger('user_id')->unsigned();
$table->timestamps();
$table->foreign('personal_access_token_id')->references('id')->on('personal_access_tokens')->cascadeOnDelete();
$table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();
});
}
用户模型得到这三个函数
public function canImpersonate()
{
return $this->is_superadmin;
}
public function canBeImpersonated()
{
return !$this->is_superadmin;
}
public function isImpersonated() {
$token = $this->currentAccessToken();
return $token->name == 'IMPERSONATION token';
}
模拟 函数。
把它放在有意义的地方。对我来说,它在我的超级管理员控制器中
public function impersonate($userId)
{
$impersonator = auth()->user();
$persona = User::find($userId);
// Check if persona user exists, can be impersonated and if the impersonator has the right to do so.
if (!$persona || !$persona->canBeImpersonated() || !$impersonator->canImpersonate()) {
return false;
}
// Create new token for persona
$personaToken = $persona->createToken('IMPERSONATION token');
// Save impersonator and persona token references
$impersonation = new Impersonation();
$impersonation->user_id = $impersonator->id;
$impersonation->personal_access_token_id = $personaToken->accessToken->id;
$impersonation->save();
// Log out impersonator
$impersonator->currentAccessToken()->delete();
$response = [
"requested_id" => $userId,
"persona" => $persona,
"impersonator" => $impersonator,
"token" => $personaToken->plainTextToken
];
return response()->json(['data' => $response], 200);
}
离开冒充
public function leaveImpersonate()
{
// Get impersonated user
$impersonatedUser = auth()->user();
// Find the impersonating user
$currentAccessToken = $impersonatedUser->currentAccessToken();
$impersonation = Impersonation::where('personal_access_token_id', $currentAccessToken->id)->first();
$impersonator = User::find($impersonation->user_id);
$impersonatorToken = $impersonator->createToken('API token')->plainTextToken;
// Logout impersonated user
$impersonatedUser->currentAccessToken()->delete();
$response = [
"requested_id" => $impersonator->id,
"persona" => $impersonator,
"token" => $impersonatorToken,
];
return response()->json(['data' => $response], 200);
}
我正在使用 Sanctum 为 Laravel 实施一个简单的模拟系统。早些时候我使用 Tymondesigns/jwt-auth with Rickycezar/laravel-jwt-impersonate,但我们最近放弃了 Tymon JWT 转而使用 Sanctum。
我没有用 Sanctum 实现 laravel-jwt-impersonate,或者它是从中分叉出来的原始 404labfr/laravel-impersonate。所以...我决定自己尝试实现一个非常简单的模拟系统。
这就是我现在要做的事情:
当管理员调用 impersonate()
函数时,我为被模拟的用户创建了一个令牌。此令牌 returned 到前端并用作不记名令牌。它似乎运行良好,在执行此操作后,应用程序就像我是模拟用户一样(因为我实际上是作为该用户“登录”的)。
public function impersonate($user)
{
$user = User::find($user);
return $user->createToken('IMPERSONATE token');
}
下一步是我想知道的。如何结束模拟。如果我注销,创建的模拟令牌将被删除,所以一切都很好......但这意味着管理员现在已注销并且必须重新登录。
我想使用之前的令牌重新登录管理员。但我不知道如何 return 管理员登录时使用的令牌。
所以我的问题:
- 你会如何解决这个问题?
- 您看到哪些明显的安全隐患(只能从管理员帐户进行模拟,并且只能模拟非管理员帐户)?
你就快完成了!我们有一个应用程序也使用 Sanctum,但使用会话而不是令牌。在我们模拟用户之前,我们将 admin/actual 用户的 ID 放入会话中:
$request->session()->put('impersonate', true); // if you need to check if session is impersonated or not
$request->session()->put('impersonate_admin_id', Auth::id());
Auth::login($user); // then impersonate
因此我们可以使用用户 ID:
在 logout/exit 模拟中重新登录到管理员Auth::loginUsingId($request->session()->get('impersonate_admin_id'));
尽管我的示例是基于会话的,但您明白了要点。由于您的是基于令牌的,并且您不能将其存储在会话或 cookie 中,因此我建议使用 DB 或 Redis/Cache.
这是我寻求的解决方案。结果比最初预期的要大一点。它似乎与我们将 Tymondesigns/jwt-auth 与 Rickycezar/laravel-jwt-impersonate 一起使用时一样有效。我对响应使用相同的结构,因此前端不需要进行任何更改。
我还在 this blog post 中概述了更多内容。
迁移
public function up()
{
Schema::create('impersonations', function (Blueprint $table) {
$table->id();
$table->bigInteger('personal_access_token_id')->unsigned();
$table->bigInteger('user_id')->unsigned();
$table->timestamps();
$table->foreign('personal_access_token_id')->references('id')->on('personal_access_tokens')->cascadeOnDelete();
$table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();
});
}
用户模型得到这三个函数
public function canImpersonate()
{
return $this->is_superadmin;
}
public function canBeImpersonated()
{
return !$this->is_superadmin;
}
public function isImpersonated() {
$token = $this->currentAccessToken();
return $token->name == 'IMPERSONATION token';
}
模拟 函数。 把它放在有意义的地方。对我来说,它在我的超级管理员控制器中
public function impersonate($userId)
{
$impersonator = auth()->user();
$persona = User::find($userId);
// Check if persona user exists, can be impersonated and if the impersonator has the right to do so.
if (!$persona || !$persona->canBeImpersonated() || !$impersonator->canImpersonate()) {
return false;
}
// Create new token for persona
$personaToken = $persona->createToken('IMPERSONATION token');
// Save impersonator and persona token references
$impersonation = new Impersonation();
$impersonation->user_id = $impersonator->id;
$impersonation->personal_access_token_id = $personaToken->accessToken->id;
$impersonation->save();
// Log out impersonator
$impersonator->currentAccessToken()->delete();
$response = [
"requested_id" => $userId,
"persona" => $persona,
"impersonator" => $impersonator,
"token" => $personaToken->plainTextToken
];
return response()->json(['data' => $response], 200);
}
离开冒充
public function leaveImpersonate()
{
// Get impersonated user
$impersonatedUser = auth()->user();
// Find the impersonating user
$currentAccessToken = $impersonatedUser->currentAccessToken();
$impersonation = Impersonation::where('personal_access_token_id', $currentAccessToken->id)->first();
$impersonator = User::find($impersonation->user_id);
$impersonatorToken = $impersonator->createToken('API token')->plainTextToken;
// Logout impersonated user
$impersonatedUser->currentAccessToken()->delete();
$response = [
"requested_id" => $impersonator->id,
"persona" => $impersonator,
"token" => $impersonatorToken,
];
return response()->json(['data' => $response], 200);
}