Slim中的身份验证:是一种智能的组合中间件,单例和挂钩方法吗?

时间:2014-09-29 20:55:32

标签: php authentication singleton slim

我已经编写了自己的身份验证控制器来在我的Slim应用程序中执行用户身份验证。虽然它有效,但我不确定这是否是Slim的工作方式。

我的身份验证控制器$auth包含$auth->login($user, $password)$auth->logout()等方法,用于更改会话状态和报告状态的方法,例如$auth->userIsLoggedIn()。此外,根据请求,它可以确定用户是否有权访问所请求的路由。

目前,我在Slim应用程序中以两种不同的方式使用$auth的单个实例:作为注册到$app->auth的单例,以及应用于所有路由的路由中间件。所以,Slim应用程序是这样引导的:

// Create singleton instance of MyAuthWrapper
$app->auth = new MyAuthenticationWrapper( array() );

// Attach the same instance as middleware to all routes
$app->add( $app->auth );

我正在使用路由中的单例实例,例如,在登录路由中:

$app->post( '/login', function() use ($app)
{
    // ...
    $user = $app->auth->authenticate( $app->request()->post('username'), $app->request()->post('password') );
    // ...
}

我在所有路由中使用中间件版本,方法是将方法附加到slim.before.dispatch挂钩,验证用户是否经过身份验证,否则重定向到登录页面。为了做到这一点,身份验证包装器扩展了\ Slim \ Middleware,从而实现了call方法,就像这样(简化):

class MyAuthenticationWrapper extends \Slim\Middleware
{
    // ... Implementation of methods such as authenticate(), isLoggedIn(), logout(), etc.

    public function call()
    {
        $app = $this->app;

        $isAuthorized = function () use ($app) {

            $hasIdentity = $this->userIsLoggedIn(); // Assume this to work
            $isAllowed = $this->userHasAccessToRequestedRoute(); // Assume this to work

            if ($hasIdentity && !$isAllowed) 
            {
                throw new Exception("You have no access to this route");
            }

            if (!$hasIdentity && !$isAllowed) 
            {
                return $app->redirect( $loginPath );
            }
        };

        $app->hook('slim.before.dispatch', $isAuthorized);

        $this->next->call();
    }
}

对我来说使用单例是轻微的code smell,但随后将单例实例添加为$app->add( $app->auth )的中间件感觉很简单。最后使用中间件向调度钩子注册一个闭包让我想知道这个整个策略对于一个名为Slim的框架是不是太复杂了。但我无法弄清楚是否有更简单或更优雅的方式来实现我想要的目标。

问题:我是否在正确的轨道上,或者我错过了一些关于Slim如何运作的内容,这样我就能以一种不太复杂的方式实现这一目标?

1 个答案:

答案 0 :(得分:6)

您使用Middleware注册一个用于身份验证的挂钩,绝对是在正确的轨道上。这就是我采用的方法以及我在自己的库Slim Auth中实现的方法。

使用Singleton肯定是代码味道,但并非总是如此。如果您觉得需要重构MyAuthenticationWrapper,那完全取决于您。您在自定义课程中使用中间件和挂钩的方式是,恕我直言,100%目标。

旁注:我的一个格言是"让它工作,然后重构。"看起来你也在那里,所以很荣幸。

最后,身份验证和授权是需要复杂解决方案的复杂主题。复杂的意味着错综复杂,难以维护的意大利面条,但正确的做法可能会产生比我希望写的更多的代码(或者比我希望的更多的依赖性)通过作曲家拉进来。)

<强>更新

如果$app->auth是中间件,那么是的,你有点偏离轨道。您创建中间件以注册挂钩的本能已经死亡,但中间件是中间件,不应该在该上下文之外使用。理想情况下,您可以创建(或者更好地在Packagist上找到一个包)一个auth类,您可以在路由和中间件中使用它们。伪代码看起来像:

$auth = new Auth(); // This is *not* middleware
$app->auth = $auth;

// Login route looks the same

// Middleware
class MyAuthenticationWrapper extends \Slim\Middleware
{
    public function call()
    {
        $app = $this->app;
        $auth = $app->auth;

        // Register your hook using appropriate methods from Auth class ...

        $this->next->call();
    }
}

这是来自Slim Auth的example Middleware。我已经把sample implementation放在一起,你可以看看我是怎么把它放在一起的。