Search code examples
phplithium

Best way to force SSL for certain controllers?


I'd like to force SSL for certain controllers, and remove SSL for everything else. I had a snippet some time ago in the _init() of a custom Controller class, it didn't work as I had hoped:

$ssl = $this->request->is('ssl');

$forceSecured = in_array($this->request->controller, array('Orders', 'Customers'));

//remove SSL for non-ssl pages
if ($ssl && !$forceSecured) {
    return $this->redirect(Router::match(
                    $this->request->params,
                    $this->request,
                    array('absolute' => true, 'scheme' => 'http://')
            )
    );
}

// Force ssl on for ssl pages
if (!$ssl && $forceSecured) {
    return $this->redirect(Router::match(
                    $this->request->params,
                    $this->request,
                    array('absolute' => true, 'scheme' => 'https://')
            )
    );
}

I'm supporting a legacy application, so I've got multiple hard-coded routes defined. I'm sure that I could use a handler in the Router::connect, but I'd rather do the check on all requests. Would continuation routes be the way to go here?


Solution

  • Two things: (1) you can't return a Response object from _init() (which is what redirect() returns), and (2) you have a subtle design problem. I'd suggest doing it like this:

    protected $_secure = false;
    
    public function __invoke($request, $params, array $options = array()) {
        if (!$this->_secure && !$request->is('ssl')) {
            return parent::__invoke($request, $params, $options);
        }
        return $this->redirect(Router::match($request->url, $request, [
            'absolute' => true, 'scheme' => 'https://'
        ]));
    }
    

    Observations:

    • You're redirecting to the same URL but for the protocol, no point in redundantly generating the URL
    • The decision of whether a particular controller gets redirected lives with the controller; this gets more important as you get to the point (and you likely will get to the point) where it's not just per-controller but per-action