PHP - 在 MVC 中的何处实现会话逻辑?
PHP - Where to implement Session Logic in MVC?
访问我的应用程序(按顺序)
- 白名单IP地址
- 在无效 ip 上重定向到 404
- 检查上次 activity 是否 > 2 小时前
- 重定向到登录页面并使会话过期
- 通过查看 $_SESSION 中的用户数据检查用户是否登录
- 如果无效则重定向到登录页面
Index.php
(注意它与 this 问题非常相似):
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new RepositoryFactory($db), new EntityFactory);
$serviceFactory->setDefaultNamespace('\MyApp\Service');
$request = new Request();
$session = new Session();
$session->start();
$router = new Router($request, $session);
/*
* Whitelist IP addresses
*/
if (!$session->isValidIp()) {
$router->import($whitelist_config);
/*
* Check if Session is expired or invalid
*/
} elseif (!$session->isValid()) {
$router->import($session_config);
/*
* Check if user is logged in
*/
} elseif (!$session->loggedIn()) {
$router->import($login_config);
} else {
$router->import($routes_config);
}
/*
* Find matched route, or throw 400 header.
*
* If matched route, add resource name
* and action to the request object.
*/
$router->route();
/*
* Initialization of View
*/
$class = '\MyApp\View\' . $request->getResourceName();
$view = new $class($serviceFactory);
/*
* Initialization of Controller
*/
$class = '\MyApp\Controller\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
echo $view->render();
$router->import()
函数获取一个包含路由配置的 json 文件并创建路由(还没有决定是否要保留它)。我的路由器是 Klein.
的修改版
我的问题
这是检查会话数据的正确实施吗?
我更愿意检查会话中的用户数据是否可以在数据库中找到,但我需要为此使用一个服务,而服务只能由控制器访问(?)。我不知道将用户发送到哪个控制器,因为如果用户已登录,路由配置会发生变化。
例如,如果有人试图去 www.myapp.com/orders/123,如果他们已登录,我会将他们发送到 Orders 控制器,或者发送到 Session 控制器(以呈现登录页)如果他们不是。
我已阅读 this 问题中的 ACL 实施。但是,除非我弄错了,否则这是为了控制已登录用户的访问权限,而不是未登录用户的访问权限。如果不是这种情况,有人可以解释一下我将如何实施我的 ACL 来检查这个?
我非常感谢任何帮助,因为搜索这个答案给了我非常复杂的解决方案,其中大多数我不喜欢或看起来不像干净的解决方案。就像会话管理器,这基本上就是我正在做的,但假装没有。 =/
已更新index.php(我的解决方案)
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new MapperFactory($db), new DomainFactory);
$serviceFactory->setDefaultNamespace('\MyApp\Service');
include CONFIG_PATH.'routes.php';
$request = new Request();
$router = new Router($routes,$request);
/*
* Find matched route.
*
* If matched route, add resource name
* and command to the request object.
*/
$router->route();
$session = $serviceFactory->create('Session');
/*
* Whitelist Ip address, check if user is
* logged in and session hasn't expired.
*/
$session->authenticate();
/*
* Access Control List
*/
include CONFIG_PATH.'acl_settings.php';
$aclFactory = new AclFactory($roles,$resources,$rules);
$acl = $aclFactory->build();
$user = $session->currentUser();
$role = $user->role();
$resource = $request->getResourceName();
$command = $request->getCommand();
// User trying to access unauthorized page
if (!$acl->isAllowed($role, $resource, $command) {
$request->setResourceName('Session');
$request->setCommand('index');
if ($role === 'blocked') {
$request->setResourceName('Error');
}
}
/*
* Initialization of View
*/
$class = '\MyApp\View\' . $request->getResourceName();
$view = new $class($serviceFactory, $acl);
/*
* Initialization of Controller
*/
$class = '\MyApp\Controller\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view, $acl);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
$view->{$command}
$view->render();
我启动会话并在会话模型中授权用户。如果未登录,会话的 currentUser 将具有 'guest' 的角色,如果其 IP 地址不在白名单中,则将具有 'blocked' 的角色。我想按照 teresko 以前的 ACL post 的建议实现控制器包装器,但我需要一些可以重定向用户的东西。如果他们试图访问不允许访问的页面,我会将他们发送到他们的主页 (Session#index),如果他们被阻止,我会将他们发送到 Error#index。 Session#index 将让 View 决定是否显示登录用户的主页,或者如果他们未登录则显示登录页面(通过检查用户的角色)。也许不是最好的解决方案,但似乎并不太糟糕。
单一职责
您的会话对象正在做太多事情。会话或多或少只是为了跨请求的持久化。会话不应执行任何身份验证逻辑。您在会话中存储登录用户的标识符,但实际验证、日志记录 in/out 应该在身份验证服务中完成。
路由管理
根据用户身份验证状态导入不同的路由不会很好地扩展,并且当您有更多路由时,稍后调试起来会很痛苦。最好在一个地方定义所有路由,如果未授权则使用身份验证服务进行重定向。我对那个路由器不是很熟悉,但是查看文档你应该能够做到
$router->respond(function ($request, $response, $service, $app) {
$app->register('auth', function() {
return new AuthService();
}
});
然后在您需要登录的路线上,您可以执行类似
的操作
$router->respond('GET', '/resource', function ($request, $response, $service, $app) {
if( ! $app->auth->authenticate() )
return $response->redirect('/login', 401);
// ...
});
访问我的应用程序(按顺序)
- 白名单IP地址
- 在无效 ip 上重定向到 404
- 检查上次 activity 是否 > 2 小时前
- 重定向到登录页面并使会话过期
- 通过查看 $_SESSION 中的用户数据检查用户是否登录
- 如果无效则重定向到登录页面
Index.php
(注意它与 this 问题非常相似):
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new RepositoryFactory($db), new EntityFactory);
$serviceFactory->setDefaultNamespace('\MyApp\Service');
$request = new Request();
$session = new Session();
$session->start();
$router = new Router($request, $session);
/*
* Whitelist IP addresses
*/
if (!$session->isValidIp()) {
$router->import($whitelist_config);
/*
* Check if Session is expired or invalid
*/
} elseif (!$session->isValid()) {
$router->import($session_config);
/*
* Check if user is logged in
*/
} elseif (!$session->loggedIn()) {
$router->import($login_config);
} else {
$router->import($routes_config);
}
/*
* Find matched route, or throw 400 header.
*
* If matched route, add resource name
* and action to the request object.
*/
$router->route();
/*
* Initialization of View
*/
$class = '\MyApp\View\' . $request->getResourceName();
$view = new $class($serviceFactory);
/*
* Initialization of Controller
*/
$class = '\MyApp\Controller\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
echo $view->render();
$router->import()
函数获取一个包含路由配置的 json 文件并创建路由(还没有决定是否要保留它)。我的路由器是 Klein.
我的问题
这是检查会话数据的正确实施吗?
我更愿意检查会话中的用户数据是否可以在数据库中找到,但我需要为此使用一个服务,而服务只能由控制器访问(?)。我不知道将用户发送到哪个控制器,因为如果用户已登录,路由配置会发生变化。
例如,如果有人试图去 www.myapp.com/orders/123,如果他们已登录,我会将他们发送到 Orders 控制器,或者发送到 Session 控制器(以呈现登录页)如果他们不是。
我已阅读 this 问题中的 ACL 实施。但是,除非我弄错了,否则这是为了控制已登录用户的访问权限,而不是未登录用户的访问权限。如果不是这种情况,有人可以解释一下我将如何实施我的 ACL 来检查这个?
我非常感谢任何帮助,因为搜索这个答案给了我非常复杂的解决方案,其中大多数我不喜欢或看起来不像干净的解决方案。就像会话管理器,这基本上就是我正在做的,但假装没有。 =/
已更新index.php(我的解决方案)
/**
* Set Timezone
*/
date_default_timezone_set('Zulu');
/**
* Include globals and config files
*/
require_once('env.php');
/*
* Closure for providing lazy initialization of DB connection
*/
$db = new Database();
/*
* Creates basic structures, which will be
* used for interaction with model layer.
*/
$serviceFactory = new ServiceFactory(new MapperFactory($db), new DomainFactory);
$serviceFactory->setDefaultNamespace('\MyApp\Service');
include CONFIG_PATH.'routes.php';
$request = new Request();
$router = new Router($routes,$request);
/*
* Find matched route.
*
* If matched route, add resource name
* and command to the request object.
*/
$router->route();
$session = $serviceFactory->create('Session');
/*
* Whitelist Ip address, check if user is
* logged in and session hasn't expired.
*/
$session->authenticate();
/*
* Access Control List
*/
include CONFIG_PATH.'acl_settings.php';
$aclFactory = new AclFactory($roles,$resources,$rules);
$acl = $aclFactory->build();
$user = $session->currentUser();
$role = $user->role();
$resource = $request->getResourceName();
$command = $request->getCommand();
// User trying to access unauthorized page
if (!$acl->isAllowed($role, $resource, $command) {
$request->setResourceName('Session');
$request->setCommand('index');
if ($role === 'blocked') {
$request->setResourceName('Error');
}
}
/*
* Initialization of View
*/
$class = '\MyApp\View\' . $request->getResourceName();
$view = new $class($serviceFactory, $acl);
/*
* Initialization of Controller
*/
$class = '\MyApp\Controller\' . $request->getResourceName();
$controller = new $class($serviceFactory, $view, $acl);
/*
* Execute the necessary command on the controller
*/
$command = $request->getCommand();
$controller->{$command}($request);
/*
* Produces the response
*/
$view->{$command}
$view->render();
我启动会话并在会话模型中授权用户。如果未登录,会话的 currentUser 将具有 'guest' 的角色,如果其 IP 地址不在白名单中,则将具有 'blocked' 的角色。我想按照 teresko 以前的 ACL post 的建议实现控制器包装器,但我需要一些可以重定向用户的东西。如果他们试图访问不允许访问的页面,我会将他们发送到他们的主页 (Session#index),如果他们被阻止,我会将他们发送到 Error#index。 Session#index 将让 View 决定是否显示登录用户的主页,或者如果他们未登录则显示登录页面(通过检查用户的角色)。也许不是最好的解决方案,但似乎并不太糟糕。
单一职责
您的会话对象正在做太多事情。会话或多或少只是为了跨请求的持久化。会话不应执行任何身份验证逻辑。您在会话中存储登录用户的标识符,但实际验证、日志记录 in/out 应该在身份验证服务中完成。
路由管理
根据用户身份验证状态导入不同的路由不会很好地扩展,并且当您有更多路由时,稍后调试起来会很痛苦。最好在一个地方定义所有路由,如果未授权则使用身份验证服务进行重定向。我对那个路由器不是很熟悉,但是查看文档你应该能够做到
$router->respond(function ($request, $response, $service, $app) {
$app->register('auth', function() {
return new AuthService();
}
});
然后在您需要登录的路线上,您可以执行类似
的操作$router->respond('GET', '/resource', function ($request, $response, $service, $app) {
if( ! $app->auth->authenticate() )
return $response->redirect('/login', 401);
// ...
});