【问题标题】:Authentication in Slim: is a combined middleware, singleton and hook approach smart?Slim 中的身份验证:组合中间件、单例和钩子方法是否智能?
【发布时间】:2016-08-12 23:59:04
【问题描述】:

我已经编写了自己的身份验证控制器来在我的 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 工作原理的东西,可以让我以不那么复杂的方式完成此任务?

【问题讨论】:

    标签: php authentication singleton slim


    【解决方案1】:

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

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

    旁注:我的座右铭之一是“让它发挥作用,然后重构”。看来你也走上正轨了,太赞了。

    最后,身份验证和授权是需要复杂解决方案的复杂主题。复杂不是意味着复杂的、难以维护的意大利面条,但如果把它做好会导致比我希望编写的代码多(或者比我希望通过 Composer 引入的依赖更多) )。

    更新

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

    $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,你可以看看我是如何整理的。

    【讨论】:

    • 谢谢杰里米!那么,创建单例然后使用 $app->add( $app->auth ) 将其添加为中间件将不是您要避免的事情吗?
    • @WilboBaggins 不。这正是我自己所做的。
    • @WilboBaggins $app->auth 是中间件吗?还是您将其注入到路由和中间件中的身份验证对象?您编写问题的方式看起来像是将中间件注入到您的路由中。
    • 是的,它是一个中间件; MyAuthenticationWrapper 类扩展了 \Slim\Middleware,它实现了 call()
    猜你喜欢
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 2011-11-25
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-23
    相关资源
    最近更新 更多