3

我试图在访问 /account 路由下的任何页面时实现强制 https。我发现这个问题ZF2 toRoute with https并且它可以工作......部分。我的路线:

'router' => array(
    'routes' => array(
        'account' => array(
            'type' => 'Scheme',
            'options' => array(
                'route' => '/account',
                'scheme' => 'https',
                'defaults' => array(
                    'controller' => 'Account\Controller\Account',
                    'action' => 'index',
                ),
            ),
            'may_terminate' => true,
            'child_routes' => array(
                'default' => array(
                    'type' => 'Literal',
                    'options' => array(
                        'route' => '/',
                        'defaults' => array(
                            'controller' => 'Account\Controller\Account',
                            'action' => 'index',
                        ),
                    ),
                ),
                'signin' => array(
                    'type' => 'Segment',
                    'options' => array(
                        'route' => '/signin[/:type]',
                        'defaults' => array(
                            'controller' => 'Account\Controller\Account',
                            'action' => 'signin',
                        ),
                        'constraints' => array(
                            'type' => '[a-zA-Z][a-zA-Z0-9-_]*',
                        ),
                    ),
                ),
                'signout' => array(
                    'type' => 'Segment',
                    'options' => array(
                        'route' => '/signout',
                        'defaults' => array(
                            'controller' => 'Account\Controller\Account',
                            'action' => 'signout',
                        ),
                    ),
                ),
                'register' => array(
                    'type' => 'Segment',
                    'options' => array(
                        'route' => '/register[/:step]',
                        'defaults' => array(
                            'controller' => 'Account\Controller\Account',
                            'action' => 'register',
                        ),
                        'constraints' => array(
                            'step' => '[a-zA-Z][a-zA-Z0-9-_]*',
                        ),
                    ),
                ),
            ),
        ),
    ),
),

以及来自 Skeleton Application 的 Application 模块的主路由(从 gi​​thub 克隆)。每当我访问 /account 的任何子路由时,它都会抛出 404:

http(s)://domain.my/account/signin = 404, wrong
http(s)://domain.my/account/* = 404, wron
https://domain.my/signin = signin page, wrong should be /account/signin
http://domain.my/ = ok, main page
http://domain.my/account = 404, wrong
https://domain.my/ = wrong, account page should be main page

通常我的问题是:该页面应该通过 http 或 https 但 /account 访问,并且它的子路由只能通过 https 访问。

编辑

好的,我已经尝试了 chained_routes 但这不是我想要实现的。我想做这样的事情:

用户未登录:类型: http ://domain.my/account -> 重定向到 https://domain.my/account/login (我知道我可以使用 实现此目的$authService->hasIdentity())然后重定向到 https://domain.my /帐户

类型: http ://domain.my/account/login -> 重定向到 https://domain.my/account/login

类型: http ://domain.my/account/edit -> 重定向到 https://domain.my/account/login 然后到https://domain.my/account/edit

与登录用户相同,当他从 /account 路由访问任何内容时,它将被重定向到相同的 url,但使用 https。

4

4 回答 4

5

如果要重定向用户,则不能使用路由执行此操作。简单来说就是先接受路由匹配,然后检查使用的scheme是不是https,如果不是,redirect。这将是控制器逻辑。因此,请忽略用例中的方案路由并检查控制器中的 https。

在 Zend Framework 1 中,我们有一个自定义帮助程序Https,如果方案是 http,您可以使用它来强制将页面重定向到 https:

public function init ()
{
    $this->https->forceHttps(array('index', 'login', 'edit'));
}

public function indexAction ()
{
    // code here
}

public function loginAction ()
{
    // code here
}

public function editAction ()
{
    // code here
}

如果您在 http 上点击索引、登录或编辑,您将被重定向到 https。如果您使用 https,则没有重定向。

目前我们没有针对 Zend Framework 2 的插件,但我认为这是您必须寻找的解决方案。使该功能成为控制器插件,以便您可以在不同的控制器之间重用它。Zend Framework 2 的示例可能更像这样:

use Zend\Http\Response;

public function loginAction()
{
    // If return value is response, this means the user will be redirected
    $result = $this->forceHttps();
    if ($result instanceof Response) {
        return $result;
    }

    // code here
}

控制器插件可能如下所示:

use Zend\Uri\Http as HttpUri;

class ForceHttps extends AbstractPlugin
{
    public function __invoke()
    {
        $request = $this->getController()->getRequest();

        if ('https' === $request->getUri()->getScheme()) {
            return;
        }

        // Not secure, create full url
        $plugin = $this->getController()->url();
        $url    = $plugin->fromRoute(null, array(), array(
            'force_canonical' => true,
        ), true);

        $url    = new HttpUri($url);
        $url->setScheme('https');

        return $this->getController()->redirect()->toUrl($url);
    }
}

注意我没有对此进行测试,因此代码中可能存在一些错误。但是你应该通过这个例子得到这个想法。

于 2013-08-10T12:10:25.013 回答
4

我遇到了类似的问题,但使用事件来解决它。重定向是一个横切关注点,如果你在每个控制器中都包含它,它就很难维护。此代码显示了如何将所有 http 请求重定向到 https。如果你只想要一些,你可以在 doHttpsRedirect() 中添加逻辑。配置文件中显示应该重定向哪些操作的数组将是添加此逻辑的一种简单方法。

class Module
{
    ...

    public function onBootstrap(MvcEvent $e){
        $em = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($em);
        $em->attach('route', array($this, 'doHttpsRedirect'));
        ...
    }

    public function doHttpsRedirect(MvcEvent $e){
        $sm = $e->getApplication()->getServiceManager();
        $uri = $e->getRequest()->getUri();
        $scheme = $uri->getScheme();
        if ($scheme != 'https'){
            $uri->setScheme('https');
            $response=$e->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $uri);
            $response->setStatusCode(302);
            $response->sendHeaders();
            return $response;
        }
    }

    ... 
}
于 2014-01-07T08:24:47.573 回答
1

TBH,从安全角度来看,如果任何页面都通过 https,那么所有这些页面都应该是,因为您不能依赖进一步的请求没有被中间人处理。

http://www.troyhunt.com/2013/05/your-login-form-posts-to-https-but-you.html

至于解决你的实际问题,我认为你对方案路由的工作方式有一个误解,看看这个来自 daspid 的公关,以获取使用 https 和链接路由https://github.com/zendframework/zf2的示例/拉/3999

于 2013-08-09T08:15:57.123 回答
0

好的,根据 Jurian Sluiman 所说的以及我在挖掘互联网时发现的内容,我已经使用控制器插件以类似的方式结束了。

模块配置:

return array(    
    'controller_plugins' => array(
        'invokables' => array(
            'ForceHttps' => 'Account\Controller\Plugin\ForceHttps',
        ),
    ),
/* rest of the configuration */

插入:

<?php
/**
 * module/Account/src/Account/Controller/Plugin/ForceHttps.php
 *
 * $Id$
 */
namespace Account\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin,
    Zend\Uri\Http as HttpUri;

class ForceHttps extends AbstractPlugin
{
    public function __invoke()
    {
        $request = $this->getController()->getRequest();

        // if we're over https then everything is ok
        if ('https' == $request->getUri()->getScheme())
        {
            return;
        }
        // Not secure, crete url and redirect only if the method is GET
        if ('GET' == $request->getMethod())
        {
            $uri = $request->getUri();

            $url = new HttpUri($uri);
            $url->setScheme('https');
            $url->setPort('443');

            return $this->getController()->redirect()->toUrl($url);
        }

        // all other methods should throw error
        throw new \Exception("ERROR [116]: Insecure connection. Please use secure connection (HTTPS)");
    }
}

控制器:

<?php
/**
 * module/Account/src/Account/Controller/AccountController.php
 *
 * $Id$
 */
namespace Account\Controller;

use Zend\Mvc\Controller\AbstractActionController,
    Zend\View\Model\ViewModel,
    Zend\EventManager\EventManagerInterface;

class AccountController extends AbstractActionController
{
    /**
     * Inject an EventManager instance
     *
     * @param EventManagerInterface $eventManager
     * @return void
     */
    public function setEventManager(EventManagerInterface $events)
    {
        parent::setEventManager($events);

        $controller = $this;

        $events->attach('dispatch', function($e) use ($controller) {
            if (($result = $controller->forceHttps()) instanceof Response) {
                return $result;
            }
        }, 100);

        return $this;
    }

    public function indexAction()
    {       
        return new ViewModel(
        );
    }

    /* rest of the actions */
}

在我使用的控制器中,setEventManager因为我读到这可以替代init()ZF1 的功能。现在,当用户通过 http 输入帐户控制器的任何操作时,它会被重定向到 https 连接,但是当他尝试通过 http 连接向帐户控制器发布内容时,他会出错。这就是我想要的。

于 2013-08-11T22:13:13.133 回答