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

时间:2016-11-29 18:59:14

标签: laravel laravel-5 laravel-5.3

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

我知道我能做的事情是:

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

返回之前政策中的

2)abort(403, '...'),由于政策已经在开展工作,这似乎有些苛刻

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

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

4 个答案:

答案 0 :(得分:1)

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

如果您创建自己的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中删除了。

答案 1 :(得分:0)

  

Laravel可以选择传递参数来自定义 authorize() 方法中通过访问的 Controller方法中的错误 Gate Facade 提供了 GateContract 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);
    
  3. 我已经做了pull request来解决这个问题,希望很快会有人合并。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

What I found was not "passing" a custom message to authorize, just defining a custom message in the policy it selfs, so, for example, if you have the method "canUseIt", in your UserPolicy, like the following:

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

You can change it and do something like this:

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

It uses the deny() method from the HandlesAuthorization trait. Then when you use it like $this->authorize('canUseIt', $user) and it fails, it will return a 403 HTTP error code with the message "Sorry man, you are not Chuck Norris".