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

Wil*_*ins 11 php authentication singleton slim

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

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

目前,我$auth在我的Slim应用程序中以两种不同的方式使用单个实例:作为注册的单例$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 );
Run Code Online (Sandbox Code Playgroud)

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

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

我在所有路由中使用中间件版本,方法是将一个方法附加到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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

问题是:我是在正确的轨道上,还是我错过了一些关于Slim如何工作的东西,这将使我能够以一种不太复杂的方式实现这一目标?

Jer*_*all 8

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

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

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

最后,认证和授权是需要复杂解决方案的复杂主题.复杂并不能意味着错综复杂,难以保持意大利面条,但得到它的权利可能会导致更多的代码比我希望写出(或更多依赖数量比我希望通过作曲家拉进来).

UPDATE

如果$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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Slim Auth 的一个中间件示例.我已经整理了一个示例实现,您可以查看我是如何将它们放在一起的.