PHP - 在MVC中实现会话逻辑的位置?

时间:2015-04-22 17:45:09

标签: php session model-view-controller architecture

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

  1. 白名单IP地址
    • 无效ip重定向到404
  2. 检查上次活动是否为> 2小时前
    • 重定向到登录页面并使会话过期
  3. 通过查看$ _SESSION中的用户数据,检查用户是否已登录
    • 如果无效,则重定向到登录页面
  4. 的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控制器(如果他们已登录),或者会话控制器(以呈现登录页面)没有。

    我已经从this问题中读到了ACL实现。但是,除非我弄错了,这是为了控制已经登录的用户的访问权限,而不是未登录的用户。如果不是这样,有人可以解释我将如何实施我的ACL来检查此?

    我非常感谢任何帮助,因为搜索这个答案给了我非常复杂的解决方案,而且大多数我不喜欢或者看起来不像是干净的解决方案。就像会话管理器一样,这基本上就是我正在做的事情,但假装没有。 = /

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

1 个答案:

答案 0 :(得分:3)

单一责任

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

路线管理

根据用户身份验证状态导入不同的路由将无法很好地扩展,如果您有更多路由,以后调试将很麻烦。最好在一个地方定义所有路由,如果未经授权,则使用身份验证服务进行重定向。我对该路由器不是很熟悉,但查看文档应该可以像

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