Slim PHP: ловите только действительные маршруты с помощью промежуточного программного обеспечения

Я пишу REST API с помощью Slim. Я написал небольшое промежуточное ПО для защиты ресурсов, чтобы только прошедшие аутентификацию пользователи могли получить к ним доступ:

<?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, вместо получения кода состояния HTTP 404 он получит 403, в котором говорится, что токена доступа нет. Я хотел бы добавить реализацию, аналогичную следующей проверке класса промежуточного программного обеспечения:

if ($app->hasRoute($app->request->getPathInfo()){
    $this->next->call(); //let go so user gets 404 from the app.
}

Есть идеи, как этого можно достичь?


person user1555863    schedule 19.02.2014    source источник


Ответы (3)


Я использую хук, чтобы делать то, что вы пытаетесь сделать, как предлагал MamaWalter, но вы хотите использовать slim.before.dispatch скорее чем более ранний крючок. Если маршрут, который пытается посетить ваш пользователь, не существует, ловушка никогда не будет вызвана и будет брошен 404.

Я делаю именно это в моем собственном person Jeremy Kendall    schedule 21.02.2014

comment
Я только что понял, что вы написали руководство, которому я следовал, и мой пример основан на вашей работе :) Хорошая работа, действительно полезна ... извините, я должен упомянуть об этом. - person MamaWalter; 21.02.2014
comment
Это потрясающе! Мне это показалось знакомым. Я подумал, что это просто случай, когда великие умы думают одинаково :-) Похоже, мне нужно обновить docblock метода, чтобы изменить рекомендуемый хук. - person Jeremy Kendall; 21.02.2014
comment
Можно ли подключить только некоторые маршруты для запуска вызова промежуточного программного обеспечения ()? - person Bagusflyer; 17.05.2014

Не совсем то, о чем вы просите, но лично, когда мне нужно проверить аутентификацию на некоторых маршрутах, я делаю это так.

config:

$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

person MamaWalter    schedule 19.02.2014
comment
Спасибо за ответ. По сути, вы делаете прямо противоположное: вместо того, чтобы отказываться от общедоступных маршрутов, вы просто перехватываете те, которые нуждаются в аутентификации. В моем случае почти все ресурсы требуют аутентификации, поэтому я не думаю, что этот подход подходит для моего варианта использования. - person user1555863; 20.02.2014

Возможно, моя реализация вам подойдет:

<?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();
        }
    }
}
person gorodezkiy    schedule 04.08.2014