Laravel 5.3在Policy失败时传递AuthorizationException消息

问题描述 投票:2回答:4

我正在尝试找到一种干净的方法来覆盖AuthorizationException以获取一个动态字符串,该字符串可以在策略失败时传回。

我知道我能做的事情是:

1)使用try-catch将策略包装在控制器中,然后重新抛出一个带有特定字符串的自定义异常,这看起来有点冗长

2)abort(403, '...')在回归之前的政策,这似乎有点hacky,因为政策已经在做工作

然后在/ Exceptions / Handler :: render我可以将响应作为JSON发回

有没有更好的方法来在策略失败的响应中获取消息?或者是1或2我最好的选择。

laravel laravel-5 laravel-5.3
4个回答
1
投票

我注意到如果你在使用Laravel异常的策略中使用throw AuthorizationException($message)它会让你跳出策略,但是继续在控制器中执行,并且不会进展到Handler::render。我假设这是他们以某种方式处理异常,但我找不到他们在做什么...所以如果有人发现这发生了什么,我仍然想知道。

如果您创建自己的AuthorizationException并抛出它,它将按预期停止执行,并放入Handler::render,所以我最终将此方法添加到我的策略中:

use App\Exceptions\AuthorizationException;

// ... removed for brevity

private function throwExceptionIfNotPermitted(bool $hasPermission = false, bool $allowExceptions = false, $exceptionMessage = null): bool
{
    // Only throw when a message is provided, or use the default 
    // behaviour provided by policies
    if (!$hasPermission && $allowExceptions && !is_null($exceptionMessage)) {

        throw new \App\Exceptions\AuthorizationException($exceptionMessage);
    }

    return $hasPermission;
}

仅在\App\Exceptions中投放策略的新例外:

namespace App\Exceptions;

use Exception;

/**
 * The AuthorizationException class is used by policies where authorization has
 * failed, and a message is required to indicate the type of failure.
 * ---
 * NOTE: For consistency and clarity with the framework the exception was named
 * for the similarly named exception provided by Laravel that does not stop
 * execution when thrown in a policy due to internal handling of the
 * exception.
 */
class AuthorizationException extends Exception
{
    private $statusCode = 403;

    public function __construct($message = null, \Exception $previous = null, $code = 0)
    {
        parent::__construct($message, $code, $previous);
    }

    public function getStatusCode()
    {
        return $this->statusCode;
    }
}

处理异常并在Handler::render()中的JSON响应中提供消息:

public function render($request, Exception $exception)
{
    if ($exception instanceof AuthorizationException && $request->expectsJson()) {

        return response()->json([
            'message' => $exception->getMessage()
        ], $exception->getStatusCode());
    }

    return parent::render($request, $exception);
}

我也把它从登录Handler::report中删除了。


0
投票

Laravel确实可以选择传递参数来自定义authorize()类的Controller方法中的错误,该类通过Gate类的GateContract Facade提供的Gate实现访问。

然而,似乎他们忘记将这些参数传递给负责返回错误消息的allow() / deny()方法,这些方法在HandlesAuthorization Trait中实现。


您需要按照以下步骤传递这些参数:

  1. 修改authorize文件中的vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php方法 public function authorize($ability, $arguments = []) { $result = $this->raw($ability, $arguments); if ($result instanceof Response) { return $result; } return $result ? $this->allow() : $this->deny($arguments); }
  2. 从控制器调用带有额外参数的authorize,即:您的自定义$message - $message = "You can not delete this comment!"; $response = $this->authorize('delete', $message);

我已经制作了一个pull request来解决这个问题,希望有人会尽快合并。


0
投票

我认为考虑策略的最佳方式是它们只是一种分离控制器逻辑的方法,并将所有与授权相关的逻辑移动到单独的文件中。因此,在大多数情况下,abort(403, 'message')是正确的方法。

唯一的缺点是您可能希望某些策略是仅用于业务逻辑的“纯”逻辑,因此不具有任何响应控制。它们可以分开,并且可以使用评论系统来区分它们。


0
投票

我发现的不是“传递”自定义消息进行授权,只是在自己的策略中定义自定义消息,例如,如果您在UserPolicy中使用方法“canUseIt”,如下所示:

public function canUseIt(User $user, MachineGun $machineGun)
    {
        if ($user->isChuckNorris()) {
            return true;
        }
        return false;
    }

你可以改变它并做这样的事情:

public function canUseIt(User $user, MachineGun $machineGun)
    {
        if ($user->isChuckNorris()) {
            return true;
        }
        $this->deny('Sorry man, you are not Chuck Norris');
    }

它使用HandlesAuthorization特征中的deny()方法。然后当你像$this->authorize('canUseIt', $user)一样使用它并且它失败时,它将返回403 HTTP错误代码,其中包含“抱歉男人,你不是查克诺里斯”的消息。

© www.soinside.com 2019 - 2024. All rights reserved.