Slim PHP:只捕获带有中间件的有效路由

时间:2014-02-19 13:30:09

标签: php slim

我正在用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.
}

有关如何实现这一目标的任何想法?

3 个答案:

答案 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();
        }
    }
}