Laravel 5.3 在策略失败时传递 AuthorizationException 消息
Posted
技术标签:
【中文标题】Laravel 5.3 在策略失败时传递 AuthorizationException 消息【英文标题】:Laravel 5.3 Passing AuthorizationException a message when a Policy fails 【发布时间】:2016-11-29 18:59:13 【问题描述】:我正在尝试找到一种干净的方法来覆盖 AuthorizationException
以获取可以在 Policy
失败时传回的动态字符串。
我知道我能做的事情是:
使用 try-catch 将 Policy
包装在 Controller
中,然后重新抛出一个采用特定字符串的自定义异常,这似乎有点冗长
abort(403, '...')
在返回之前在Policy
中,这似乎有点老套,因为政策已经在起作用了
然后在/Exceptions/Handler::render
中,我可以将响应以 JSON 格式发回
有没有更好的方法来获得响应策略失败的消息?还是 1 或 2 是我最好的选择。
【问题讨论】:
那么如果引发了 AuthorizationException,您想生成自定义错误吗? 嗨@atefth yah有点像验证失败并且您收到错误包时,但在这种情况下,您将收到一个失败的策略包,其中包含一条消息,该消息会根据失败的策略而有所不同JSON 响应。 【参考方案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
中删除。
【讨论】:
【参考方案2】:我发现并没有将自定义消息“传递”给 authorize,只是在它自己的策略中定义自定义消息,因此,例如,如果您有方法“canUseIt”,在您的 UserPolicy,如下所示:
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 错误代码和消息“对不起,你不是 Chuck Norris”。
【讨论】:
【参考方案3】:Laravel 确实有一个选项可以传递参数来自定义
authorize()
方法中的错误,该方法是通过Gate
访问的Controller
类Gate
Facade 提供的GateContract
类的实现。但是,他们似乎忘记将这些参数传递给
allow()
/deny()
负责返回错误消息的方法,在 中实现HandlesAuthorization
特质.
您需要按照以下步骤传递这些参数:
修改vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php
文件中的authorize
方法
public function authorize($ability, $arguments = [])
$result = $this->raw($ability, $arguments);
if ($result instanceof Response)
return $result;
return $result ? $this->allow() : $this->deny($arguments);
使用额外参数从控制器调用 authorize
,即:您的自定义 $message
-
$message = "You can not delete this comment!";
$response = $this->authorize('delete', $message);
我已经创建了一个pull request 来解决这个问题,希望有人能尽快合并它。
【讨论】:
很好,我们正在使用模型策略$user->can(...);
,所以我会寻找这种相同类型的解决方案,看看它是否有效。此外,您的 PR 似乎已被暂时/永久保留 :(【参考方案4】:
我认为考虑策略的最佳方式是它们只是一种拆分控制器逻辑的方法,并将所有与授权相关的逻辑移动到单独的文件中。因此,在大多数情况下,abort(403, 'message')
是正确的方法。
唯一的缺点是您可能希望某些策略是“纯”逻辑,仅用于业务逻辑,因此没有任何响应控制。它们可以分开保存,并且可以使用评论系统来区分它们。
【讨论】:
以上是关于Laravel 5.3 在策略失败时传递 AuthorizationException 消息的主要内容,如果未能解决你的问题,请参考以下文章
从 curl 使用 Auth0 调用 Laravel 5.3 API 时未经授权的用户
为啥 Auth::attempt 在 Laravel 5.3 中总是返回 false?
从 5.1 迁移到 5.3 时急切加载关系的 Laravel 错误