PHP - 在 MVC 中在哪里实现 session 逻辑?

标签 php session model-view-controller architecture

访问我的应用程序(按顺序)

  1. 白名单IP地址
    • 在无效 ip 上重定向到 404
  2. 检查上次事件是否 > 2 小时前
    • 重定向到登录页面并终止 session
  3. 通过查看 $_SESSION 中的用户数据检查用户是否登录
    • 如果无效则重定向到登录页面

索引.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 的修改版.

我的问题

这是检查 session 数据的正确实现吗?

我更愿意检查 session 中的用户数据是否可以在数据库中找到,但我需要为此使用一个服务,而服务只能由 Controller 访问(?)。我不知道将用户发送到哪个 Controller ,因为如果用户已登录,路由配置会发生变化。

例如,如果有人试图访问 www.myapp.com/orders/123,如果他们已登录,我会将他们发送到 Orders Controller ,或者如果他们发送到 Session Controller (以呈现登录页面)不是。

我已阅读 this 中的 ACL 实现问题。但是,除非我弄错了,否则这是为了控制已登录用户的访问权限,而不是未登录用户的访问权限。如果不是这种情况,有人可以解释一下我将如何实现我的 ACL 来检查这个?

我非常感谢任何帮助,因为搜索这个答案给了我真正混合的解决方案,其中大多数我不喜欢或看起来不像干净的解决方案。就像 session 管理器,这基本上就是我正在做的,但假装没有。 =/

已更新 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();

我启动 session 并在 session 模型中授权用户。如果未登录, session 的 currentUser 将具有“访客”角色,如果其 IP 地址不在白名单中,则将具有“阻止”角色。我想按照 teresko 之前的 ACL 帖子的建议实现 Controller 包装器,但我需要一些可以重定向用户的东西。如果他们试图访问不允许访问的页面,我会将他们发送到他们的主页 (Session#index),如果他们被阻止,我会将他们发送到 Error#index。 Session#index 将让 View 决定是否显示登录用户的主页,或者如果他们未登录则显示登录页面(通过检查用户的角色)。也许不是最好的解决方案,但似乎并不太糟糕。

最佳答案

单一职责

您的 session 对象正在做太多事情。 session 或多或少只是为了跨请求的持久化。 session 不应执行任何身份验证逻辑。您在 session 中存储登录用户的标识符,但实际验证、登录/注销应该在身份验证服务中完成。

路由管理

根据用户身份验证状态导入不同的路由不会很好地扩展,并且当您有更多路由时,稍后调试起来会很痛苦。最好在一个地方定义所有路由,如果未授权则使用身份验证服务进行重定向。我对那个路由器不是很熟悉,但查看文档你应该能够做到类似

$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);
    // ...
});

关于PHP - 在 MVC 中在哪里实现 session 逻辑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29805213/

相关文章:

java - Spring 4和Hibernate 4 Session错误

javascript - MVC中通过Jquery从隐藏字段获取多个值

c# - 使用自定义成员资格和角色提供者在 MVC 中实现 IPrincipal 和 IIdentity

css - Bootstrap 没有 'Access-Control-Allow-Origin' header

javascript - 有没有办法用 stdClass 从 php 到 javascript 响应匿名函数

php - 记录插入和输出取决于它的值

php - mySQL 查询 - 显示最受欢迎的项目

php - Laravel 4-重试所有失败的作业

PHP 高级安全 session 类

javascript - session 丢失 window.location.href