我正在用Slim编写REST API。我编写了一个小型中间件来保护资源,因此只有经过身份验证的用户才能访问它们:
<?php
class SecurityMiddleware extends \Slim\Middleware
{
protected $resource;
public function __construct($resource)
{
$this->resource = $resource;
}
public function call()
{
//get a reference to application
$app = $this->app;
//skip routes that are exceptionally allowed without an access token:
$publicRoutes = ["/","/login","/about"];
if (in_array($app->request()->getPathInfo(),publicRoutes)){
$this->next->call(); //let go
} else {
//Validate:
if ($this->resource->isValid()){
$this->next->call(); //validation passed, let go
} else {
$app->response->setStatus('403'); //validation failed
$app->response->body(json_encode(array("Error"=>"Access token problem")));
return;
}
}
}
}
这样可行,但不良副作用是中间件不区分现有路由和不存在的路由。例如,如果用户尝试请求不存在的/dfghdfgh
之类的路由,而不是获取404的HTTP状态代码,则会得到403表示没有访问令牌。我想在中间件类中添加类似于以下检查的实现:
if ($app->hasRoute($app->request->getPathInfo()){
$this->next->call(); //let go so user gets 404 from the app.
}
有关如何实现这一目标的任何想法?
答案 0 :(得分:5)
我使用hook来做你正在尝试做的事情,就像MamaWalter建议的那样,但你想使用slim.before.dispatch
而不是早先的钩子。如果您的用户尝试访问的路由不存在,则永远不会调用该挂钩并且404
将被抛出。
我正在我自己的Authorization Middleware做到这一点。像魅力一样。
答案 1 :(得分:2)
不完全是你要求的,但是当我需要检查某些路线上的身份验证时,我会这样做。
配置:
$config = array(
...,
'user.secured.urls' => array(
array('path' => '/user'),
array('path' => '/user/'),
array('path' => '/user/.+'),
array('path' => '/api/user/.+')
),
...
);
中间件:
/**
* Uses 'slim.before.router' to check for authentication when visitor attempts
* to access a secured URI.
*/
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
$checkAuth = function () use ($app, $auth, $req, $config) {
// User restriction
$userSecuredUrls = isset($config['user.secured.urls']) ? $config['user.secured.urls'] : array();
foreach ($userSecuredUrls as $url) {
$urlPattern = '@^' . $url['path'] . '$@';
if (preg_match($urlPattern, $req->getPathInfo()) === 1 && $auth->hasIdentity() === false) {
$errorData = array('status' => 401,'error' => 'Permission Denied');
$app->render('error.php', $errorData, 401);
$app->stop();
}
}
};
$app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
但是,如果您的几乎所有路线都需要验证,则可能不是最佳解决方案。
很好的例子:http://www.slideshare.net/jeremykendall/keeping-it-small-slim-php
答案 2 :(得分:2)
也许我的实施对您有用:
<?php
class CustomAuth extends \Slim\Middleware {
public function hasRoute() {
$dispatched = false;
// copied from Slim::call():1312
$matchedRoutes = $this->app->router->getMatchedRoutes($this->app->request->getMethod(), $this->app->request->getResourceUri());
foreach ($matchedRoutes as $route) {
try {
$this->app->applyHook('slim.before.dispatch');
$dispatched = $route->dispatch();
$this->app->applyHook('slim.after.dispatch');
if ($dispatched) {
break;
}
} catch (\Slim\Exception\Pass $e) {
continue;
}
}
return $dispatched;
}
public function call() {
if ($this->hasRoute()) {
if ($authorized) {
$this->next->call();
}
else {
$this->permissionDenied();
}
}
else {
$this->next->call();
}
}
}