我已经编写了自己的身份验证控制器来在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 );
我正在使用路由中的singleton实例,例如,在登录路由中:
$app->post( '/login', function() use ($app)
{
// ...
$user = $app->auth->authenticate( $app->request()->post('username'), $app->request()->post('password') );
// ...
}
我在所有路由中使用中间件版本,方法是在slim.before.dispatch
钩子上附加一个方法,验证用户是否通过了身份验证,否则重定向到登录页面。为了做到这一点,身份验证包装器扩展了'' Slim''中间件,从而实现了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();
}
}
对我来说,使用singleton是一种轻微的代码气味,但使用$app->add( $app->auth )
将singleton实例添加为中间件会让人觉得很恶心。最后,使用中间件向调度钩子注册一个闭包,这让我怀疑对于一个名为Slim的框架来说,整个策略是否太复杂。但我不知道是否有更简单或更优雅的方式来实现我想要的。
问题:我是否走在了正确的轨道上,或者我是否错过了Slim的工作方式,这将使我能够以一种不那么复杂的方式完成这项工作?
使用中间件注册钩子进行身份验证绝对是正确的。这就是我采用的方法,也是我在自己的库Slim Auth中实现的方法。
使用Singleton肯定是一种代码气味,但并不总是如此。如果您觉得需要重构MyAuthenticationWrapper,那完全取决于您。您在自定义类中使用中间件和Hooks的方式是,IMHO,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的中间件示例。我已经将一个示例实现放在一起,您可以查看我是如何将其放在一起的。