【问题标题】:Impersonate users with Sanctum使用 Sanctum 模拟用户
【发布时间】:2021-12-17 17:38:43
【问题描述】:

我正在使用 Sanctum 为 Laravel 实现一个简单的模拟系统。之前我使用 Tymondesigns/jwt-authRickycezar/laravel-jwt-impersonate,但我们最近放弃了 Tymon JWT 用于 Sanctum。

我没有运气用 Sanctum 或原来的 404labfr/laravel-impersonate 实现 laravel-jwt-impersonate,它是从它派生出来的。所以...我决定自己尝试实现一个非常简单的模拟系统。

这就是我现在要做的:

当管理员调用impersonate() 函数时,我会为被模拟的用户创建一个令牌。该令牌返回到前端并用作不记名令牌。它似乎运行良好,并且在执行此操作后,应用程序就像我是模拟用户一样(因为我实际上是以该用户的身份“登录”的)。

public function impersonate($user)
{
    $user = User::find($user);
    return $user->createToken('IMPERSONATE token');
}

下一步是我想知道的。如何结束模仿。如果我注销,创建的模拟令牌将被删除,所以一切都很好......但这意味着管理员现在已经注销并且必须重新登录。

我想使用较早的令牌重新登录管理员。但我不知道如何返回管理员登录时使用的令牌。

所以我的问题:

  1. 您将如何解决这个问题?
  2. 您发现哪些明显的安全风险(仅允许从管理员帐户进行模拟,并且只能模拟非管理员)?

【问题讨论】:

    标签: laravel impersonation laravel-sanctum


    【解决方案1】:

    你快到了!我们有一个应用程序也使用 Sanctum,但使用会话而不是令牌。在模拟用户之前,我们将管理员/实际用户的 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 在注销/退出模拟时重新登录到管理员:

    Auth::loginUsingId($request->session()->get('impersonate_admin_id'));
    

    虽然我的示例是基于会话的,但您明白了要点。由于您的是基于令牌的,并且您不能将其存储在会话或 cookie 中,我建议使用 DB 或 Redis/Cache。

    【讨论】:

    • 谢谢。不幸的是,它并没有真正为我解决这个问题,但它把我推向了正确的方向。我将访问令牌和模拟者之间的引用保存在一个新表中。然后我只是在模拟时注销管理员并在离开模拟时创建一个新令牌。
    【解决方案2】:

    这是我寻求的解决方案。结果比最初预期的要大一点。它似乎和我们使用 Tymondesigns/jwt-auth 和 Rickycezar/laravel-jwt-impersonate 时一样好。我对响应使用相同的结构,因此前端不需要进行任何更改。

    迁移

    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();
        });
    }
    

    User模型获取这三个函数

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 2020-11-06
      • 2015-07-09
      • 1970-01-01
      • 2012-12-22
      • 1970-01-01
      • 2020-10-22
      • 1970-01-01
      • 2014-12-21
      相关资源
      最近更新 更多