php - cakePHP在模型中使用请求/身份验证数据的3个最佳实践(基于帐户的云服务)

原文 标签 php session cakephp cakephp-3.0

cakePHP 3 best practises for using request/auth data in model (account based cloud service)

I'm creating a cloud service where there are multiple accounts and each account has multiple users. Users can only log into their account. Each account has an unique alias. (e.g. abc). If users go to their sub-domain (e.g. abc.cloud.service) they are able to log into their account and see only the stuff which is assigned to that account. The login works very well.

I have a htaccess redirection rule added which transforms the sub-domain to a query.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{HTTP_HOST} ^([a-z0-9]+)\.cloud\.service$ [NC]
RewriteRule ^ index.php?alias=%1 [L]

So in all controllers I have the alias as a query available. In order to guarantee that users are only allowed to see their contents all models are related to an account. Every time I want to perform a find operation on one of those models I want to make sure that the results belong to the correct account. I'm doing that in the beforeFind.

// beforeFind of models which are related to an account
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
    if ($primary) {
        $query->contain(['Accounts']);
    }
}

And to get the correct account I have a beforeFind in the Accounts model where I check the query.

// beforeFind of the Accounts model
public function beforeFind (Event $event, Query $query, ArrayObject $options, $primary) {
    if (isset($_GET['alias'])) {
        $query->where(['Accounts.alias' => $_GET['alias']]);
    }
}

This works amazingly well as all my find operations are returning only data which is allowed for the that account. But I know what I'm doing is probably not called best practice. Question 1: So is there a better way of doing that? I don't know how to access the query data in the model?

Problem 2: When I save any type of content I need to make sure it is assigned to the correct account.

Option 1 (ineffective due to too many occurrences) I could manually assign the accound id before all save calls depending on the users account id. But I'm going to have too many save calls, so I would like to have a better solution here.

Option 2 (is what I'm doing right now) I add some code to the beforeSave to all models which are related to the account:

// beforeSave in models related to Accounts model
public function beforeSave(Event $event, Entity $entity, ArrayObject $options)
{
    if (!isset($_SESSION['Auth']['User']['account_id'])) {
        $entity->account_id = $_SESSION['Auth']['User']['account_id'];
    }
}

Works again very well, but I also know that accessing the session variable directly is not recommended by cakePHP. Question 2, is there a better solution here? I didn't find a working solution on accessing auth data in models

Answer

First of all, I would say accessing authorization information within your models is not best practice because it doesn't enforce a good level of separation of concerns between controller logic and business logic.

I would say the best case to use here would be a dispatch filter, perhaps something like:

class SubdomainFilter extends DispatcherFilter
{
    public function beforeFilter(Event $event)
    {
        if (isset($_GET['alias'])) {
            $event->data['request']->subdomain_alias = $_GET['alias'];
        }
    }
}

Then initialize your new filter in bootstrap.php:

DispatchFactory::add('SubdomainFilter');

Then finally you can access the subdomain alias as a property attached to your request object in your controller to be passed as an argument into your model functions:

class FooController extends AppController
{
    public function exampleEndpoint() {
        $your_table->findFunction($this->request->subdomain_alias);
        $your_table->saveFunction($this->request->subdomain_alias, $data);
}

You can read more about Dispatch Filters in CakePHP 3 here.

翻译

我正在创建一个云服务,其中有多个帐户,每个帐户有多个用户。用户只能登录其帐户。每个帐户都有一个唯一的别名。 (例如abc)。如果用户转到其子域(例如abc.cloud.service),则可以登录其帐户并仅查看分配给该帐户的内容。登录效果很好。

我添加了htaccess重定向规则,该规则将子域转换为查询。

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{HTTP_HOST} ^([a-z0-9]+)\.cloud\.service$ [NC]
RewriteRule ^ index.php?alias=%1 [L]


因此,在所有控制器中,我都有别名作为可用查询。为了保证只允许用户查看其内容,所有模型都与一个帐户相关。每当我想对其中一个模型执行查找操作时,我都想确保结果属于正确的帐户。我在beforeFind中这样做。

// beforeFind of models which are related to an account
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
    if ($primary) {
        $query->contain(['Accounts']);
    }
}


为了获得正确的帐户,我在“帐户”模型中有一个beforeFind,用于检查查询。

// beforeFind of the Accounts model
public function beforeFind (Event $event, Query $query, ArrayObject $options, $primary) {
    if (isset($_GET['alias'])) {
        $query->where(['Accounts.alias' => $_GET['alias']]);
    }
}


由于我所有的查找操作都只返回该帐户允许的数据,因此效果很好。但是我知道我在做什么可能不被称为最佳实践。问题1:有没有更好的方法?我不知道如何访问模型中的查询数据?

问题2:
保存任何类型的内容时,我需要确保将其分配给正确的帐户。

选项1(由于发生过多而无效)
我可以在所有保存呼叫之前手动分配Accound ID,具体取决于用户帐户ID。但是我将有太多的保存呼叫,所以我想在这里有一个更好的解决方案。

选项2(这是我现在正在做的)
我将一些代码添加到beforeSave到与该帐户相关的所有模型中:

// beforeSave in models related to Accounts model
public function beforeSave(Event $event, Entity $entity, ArrayObject $options)
{
    if (!isset($_SESSION['Auth']['User']['account_id'])) {
        $entity->account_id = $_SESSION['Auth']['User']['account_id'];
    }
}


再次可以很好地工作,但是我也知道cakePHP不建议直接访问会话变量。问题2,这里有更好的解决方案吗?我找不到在模型中访问身份验证数据的有效解决方案
最佳答案
首先,我想说的是,在您的模型中访问授权信息不是最佳实践,因为这不会在控制器逻辑和业务逻辑之间实现高级别的关注点分离。

我会说,在这里使用的最佳情况是调度过滤器,也许是这样的:

class SubdomainFilter extends DispatcherFilter
{
    public function beforeFilter(Event $event)
    {
        if (isset($_GET['alias'])) {
            $event->data['request']->subdomain_alias = $_GET['alias'];
        }
    }
}


然后在bootstrap.php中初始化新的过滤器:

DispatchFactory::add('SubdomainFilter');


然后,最后您可以将子域别名作为属性附加到控制器中的请求对象,并作为参数传递给模型函数:

class FooController extends AppController
{
    public function exampleEndpoint() {
        $your_table->findFunction($this->request->subdomain_alias);
        $your_table->saveFunction($this->request->subdomain_alias, $data);
}


您可以在CakePHP 3 here中了解有关调度过滤器的更多信息。
相关推荐

php - 什么数据类型可用于在mysql中放置标志(布尔值)?

javascript - 尝试将数据从.php文件传递到.js文件

php - 更改子菜单时如何更改网页背景?

php - Symfony2:如何避免数据库中已经存在重复的实体?

php - Eclipse中的PHP 7调试器设置

javascript - 访问URL参数-PHP和Angular.js

php - 使用更新的MySQL库修复PHP数据库连接失败

php - PHP代码不允许多个输入值通过我的表单发送

php - Laravel 5.1内部联接查询中的问题

php - 控制PHP ldap分页-结果数和页码