Slim3 从 CSRF 中间件中排除路由
Slim3 exclude route from CSRF Middleware
我正在构建一个基于 slim3 框架的网上商店。我需要处理服务器到服务器的 POST 请求以确认付款是否成功。我像这样将 csrf 添加到容器中:
$container['csrf'] = function($container) {
return new \Slim\Csrf\Guard;
};
并像这样将其添加到应用程序中:
$app->add($container->csrf);
而且效果很好。但现在我需要能够向特定路线添加例外,以便我收到他们发送的 post 请求。
到目前为止,我找不到可行的解决方案。
有什么建议吗?
如果需要从中间件中排除一条路由,有两种选择:
选项 1:将您的路线分组。
您可以将 所有 条路线分组,但以下路线除外:
<?php
$app->group('', function() {
// All routes declarations....
})->add($container->csrf); // Add middleware to all routes within the group
// Declare your "exceptional" route outside the group
$app->post('my-special-route-that-has-no-csrf-middleware', 'routeProcessor');
方案二:使用自己的中间件
不要直接使用 \Slim\Csrf\Guard
,而是使用您自己的中间件来扩展它。您的中间件将检查路由,如果路由是 "exceptional",它将跳过。
将此添加到设置中,因为您需要在中间件中访问路由:
$container['settings'] => [
'determineRouteBeforeAppMiddleware' => true
];
创建扩展原始 \Slim\Csrf\Guard
的中间件:
<?php
class MyCsrfMiddleware extends Slim\Csrf\Guard
{
// This method is processing every request in your application
public function processRequest($request, $response, $next) {
// Check if it's your "exceptional" route
$route = $request->getAttribute('route');
if ($route == 'my-special-path') {
// If it is - just pass request-response to the next callable in chain
return $next($request, $response);
} else {
// else apply __invoke method that you've inherited from \Slim\Csrf\Guard
return $this($request, $response, $next);
}
}
}
/////////////
$container['csrf'] = function($container) {
return new MyCsrfMiddleware; // Now the container returns your middleware under 'csrf' key
};
现在只需将中间件添加到 \Slim\App
实例:
$app->add('csrf:processRequest');
如果有人还在为此烦恼(特别是如果你想使用 webhooks 的话)。
我在 Georgy 的回答的帮助下找到了一个更简单的解决方案。
对实际的Slim\Csrf\Guard'Guard.php'文件及其__invoke[=19=做如下修改即可] 方法。或者只需复制并粘贴下面的代码...
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$route = $request->getAttribute('routeInfo');
$routeRequestInfo = $route['request'];
$requestUrl = $routeRequestInfo[1];
if ($requestUrl == 'http://yoursite/the-url-you-want-to-exempt')
{
//This will just return the request to your application with applying the csrf.
return $next($request, $response);
}
else
{
$this->validateStorage();
// Validate POST, PUT, DELETE, PATCH requests
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$body = $request->getParsedBody();
$body = $body ? (array)$body : [];
$name = isset($body[$this->prefix . '_name']) ? $body[$this->prefix . '_name'] : false;
$value = isset($body[$this->prefix . '_value']) ? $body[$this->prefix . '_value'] : false;
if (!$name || !$value || !$this->validateToken($name, $value)) {
// Need to regenerate a new token, as the validateToken removed the current one.
$request = $this->generateNewToken($request);
$failureCallable = $this->getFailureCallable();
return $failureCallable($request, $response, $next);
}
}
// Generate new CSRF token if persistentTokenMode is false, or if a valid keyPair has not yet been stored
if (!$this->persistentTokenMode || !$this->loadLastKeyPair()) {
$request = $this->generateNewToken($request);
}
// Enforce the storage limit
$this->enforceStorageLimit();
}
return $next($request, $response);
}
$container['csrf'] = function($container) {
$guard = new \Slim\Csrf\Guard;
$guard->setFailureCallable(function($request, $response, $next) use ($container) {
$request = $request->withAttribute("csrf_status", false);
if($request->getAttribute('csrf_status') === false) {
if($request->getAttribute('route')->getName()=== 'your-route-name'){
return $next($request, $response);
}
return $response->withStatus(400)->withRedirect($container['router']->pathFor('home'));
} else {
return $next($request, $response);
}
});
return $guard;
};
我正在构建一个基于 slim3 框架的网上商店。我需要处理服务器到服务器的 POST 请求以确认付款是否成功。我像这样将 csrf 添加到容器中:
$container['csrf'] = function($container) {
return new \Slim\Csrf\Guard;
};
并像这样将其添加到应用程序中:
$app->add($container->csrf);
而且效果很好。但现在我需要能够向特定路线添加例外,以便我收到他们发送的 post 请求。 到目前为止,我找不到可行的解决方案。
有什么建议吗?
如果需要从中间件中排除一条路由,有两种选择:
选项 1:将您的路线分组。
您可以将 所有 条路线分组,但以下路线除外:
<?php
$app->group('', function() {
// All routes declarations....
})->add($container->csrf); // Add middleware to all routes within the group
// Declare your "exceptional" route outside the group
$app->post('my-special-route-that-has-no-csrf-middleware', 'routeProcessor');
方案二:使用自己的中间件
不要直接使用 \Slim\Csrf\Guard
,而是使用您自己的中间件来扩展它。您的中间件将检查路由,如果路由是 "exceptional",它将跳过。
将此添加到设置中,因为您需要在中间件中访问路由:
$container['settings'] => [
'determineRouteBeforeAppMiddleware' => true
];
创建扩展原始 \Slim\Csrf\Guard
的中间件:
<?php
class MyCsrfMiddleware extends Slim\Csrf\Guard
{
// This method is processing every request in your application
public function processRequest($request, $response, $next) {
// Check if it's your "exceptional" route
$route = $request->getAttribute('route');
if ($route == 'my-special-path') {
// If it is - just pass request-response to the next callable in chain
return $next($request, $response);
} else {
// else apply __invoke method that you've inherited from \Slim\Csrf\Guard
return $this($request, $response, $next);
}
}
}
/////////////
$container['csrf'] = function($container) {
return new MyCsrfMiddleware; // Now the container returns your middleware under 'csrf' key
};
现在只需将中间件添加到 \Slim\App
实例:
$app->add('csrf:processRequest');
如果有人还在为此烦恼(特别是如果你想使用 webhooks 的话)。
我在 Georgy 的回答的帮助下找到了一个更简单的解决方案。
对实际的Slim\Csrf\Guard'Guard.php'文件及其__invoke[=19=做如下修改即可] 方法。或者只需复制并粘贴下面的代码...
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$route = $request->getAttribute('routeInfo');
$routeRequestInfo = $route['request'];
$requestUrl = $routeRequestInfo[1];
if ($requestUrl == 'http://yoursite/the-url-you-want-to-exempt')
{
//This will just return the request to your application with applying the csrf.
return $next($request, $response);
}
else
{
$this->validateStorage();
// Validate POST, PUT, DELETE, PATCH requests
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$body = $request->getParsedBody();
$body = $body ? (array)$body : [];
$name = isset($body[$this->prefix . '_name']) ? $body[$this->prefix . '_name'] : false;
$value = isset($body[$this->prefix . '_value']) ? $body[$this->prefix . '_value'] : false;
if (!$name || !$value || !$this->validateToken($name, $value)) {
// Need to regenerate a new token, as the validateToken removed the current one.
$request = $this->generateNewToken($request);
$failureCallable = $this->getFailureCallable();
return $failureCallable($request, $response, $next);
}
}
// Generate new CSRF token if persistentTokenMode is false, or if a valid keyPair has not yet been stored
if (!$this->persistentTokenMode || !$this->loadLastKeyPair()) {
$request = $this->generateNewToken($request);
}
// Enforce the storage limit
$this->enforceStorageLimit();
}
return $next($request, $response);
}
$container['csrf'] = function($container) {
$guard = new \Slim\Csrf\Guard;
$guard->setFailureCallable(function($request, $response, $next) use ($container) {
$request = $request->withAttribute("csrf_status", false);
if($request->getAttribute('csrf_status') === false) {
if($request->getAttribute('route')->getName()=== 'your-route-name'){
return $next($request, $response);
}
return $response->withStatus(400)->withRedirect($container['router']->pathFor('home'));
} else {
return $next($request, $response);
}
});
return $guard;
};