【问题标题】:ZF2 and force HTTPS for specific routesZF2 并针对特定路由强制使用 HTTPS
【发布时间】:2013-08-09 06:26:10
【问题描述】:

我正在尝试在访问 /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/account

类型: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。

【问题讨论】:

  • 对我来说,不清楚你想要什么(目标)以及你的实际结果是什么。你能简单地说我想达到 X,所以我做了 Y,但结果是 Z。你现在得到的 404 是理想的结果吗?
  • 经过一番思考后,目标很简单:帐户控制器中的所有内容 - 因此以 /account 开头的路由 - 必须只能通过 https 访问,当通过 http 访问它们时,它们会被重定向到 https。这既简单又复杂……
  • 所以请看下面我的回答。您不应该只接受通过 https 的路由,因为您必须接受 http 版本才能执行重定向。我建议使用控制器插件,请参阅下面的示例

标签: https routes zend-framework2


【解决方案1】:

如果你想重定向一个用户,你不能用路由来做这件事。简单地说,你必须先接受路由匹配,然后检查使用的方案是否为https,如果不是,则重定向。这将是控制器逻辑。因此,请忽略用例中的方案路由并在控制器中检查 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);
    }
}

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

【讨论】:

  • 我和原发帖人有同样的问题,我发现实际上,使用bitbucket.org/netglue/zf2-require-ssl-module,我可以让它像原发帖人一样工作,在路线配置中。通过 composer 添加该模块后,您可以将 'force-ssl' => 'ssl' 放入您的路由默认值中,它就像一个魅力:)
  • @RobertHyatt 我用 .htaccess 尝试了很多,但没有成功。但是“ssl-module”就像魅力一样!感谢 RobertHyatt 分享链接 :)
【解决方案2】:

我遇到过类似的问题,但使用事件解决了它。重定向是一个横切关注点,如果你在每个控制器中包含它就很难维护。此代码显示了如何将所有 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;
        }
    }

    ... 
}

【讨论】:

    【解决方案3】:

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

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

    至于解决您的实际问题,我认为您对方案路由的工作方式有误解,请查看 daspid 的此 pr 以获取使用 https 和链接路由的示例https://github.com/zendframework/zf2/pull/3999

    【讨论】:

    • 试过了,但它没有按我的意愿工作。我也更新了我的问题。或者也许有人可以解释我应该怎么做。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2017-08-22
      • 2012-02-24
      • 2019-12-06
      • 1970-01-01
      • 2013-09-27
      • 2016-06-20
      • 1970-01-01
      • 2018-06-23
      相关资源
      最近更新 更多