Welcome Guest, Not a member yet? Register   Sign In
Forcing HTTPS for certain routes using customised Config class and hook
#1

[eluser]Andrew G.[/eluser]
It is quite common that on a site there a certain urls that should only be accessible via https. This requires:
- making sure that whenever a link to that page is created it points to the https:// address
- if an attempt is made to access it at its non-https address the user is automatically redirected to the https address

I have implemented such a solution by customising the Config class so that the site_url function will create https urls as required and created a hook so that any http requests to the configured pages are redirected to https.

For this solution to work you must create all the links to <em>page urls</em> using the site_url method defined in the url helper which should be auto loaded. (By <em>page urls</em> I mean urls that people click on or forms post to rather than urls for js, css, image files etc which should be relative so they work on http and https pages).

Both the MY_Config class and the hook use a config file called 'ssl.php'. This file defines routes as follows:

Code:
$config['ssl_routes'] = array(
    'account/settings',
    'account/sign_in',
    'account/create/:any'
    );

Note the use of ':any' which works the same way it does in the routes.php file. (as does :num)

MyConfig.php looks like this:
Code:
class MY_Config extends CI_Config {


    protected $_ssl_routes = null;

    /**
     * Site URL
     *
     * @access    public
     * @param    string    the URI string
     * @return    string
     */
    function site_url($uri = '')
    {
        if (is_array($uri))
        {
            $uri = implode('/', $uri);
        }

        //load routes from config if not already loaded
        $this->_ssl_routes = $this->ssl_routes();

        //if there are no ssl routes then just call the parent.
        if ($this->_ssl_routes === FALSE) return parent::site_url($uri);


        //see if the current url matches any of the routes        
        foreach ($this->_ssl_routes as $route)
        {
            if (preg_match('#^'.$route.'$#', $uri))
            {            
                $ssl_base_url = str_replace('http://', 'https://', $this->slash_item('base_url'));

                if ($uri == '')
                {
                    return $base_url.$this->item('index_page');
                }
                else
                {
                    $suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
                    return $ssl_base_url.$this->slash_item('index_page').preg_replace("|^/*(.+?)/*$|", "\\1", $uri).$suffix;
                }
            }
        }

        return parent::site_url($uri);
    }

    function ssl_routes()
    {
        if (is_null($this->_ssl_routes))
        {
            $this->_ssl_routes = $this->item('ssl_routes');

            if (is_array($this->_ssl_routes))
            {
                for ($i = 0; $i < count($this->_ssl_routes); $i++)
                {
                    // Convert wild-cards to RegEx
                    $this->_ssl_routes[$i] = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $this->_ssl_routes[$i]));
                }
            }
        }

        return $this->_ssl_routes;
    }
}

This file needs to be in application/libraries/ as it overrides the default Config class


Finally, there is the 'post_controller_constructor' hook defined in application/config/hooks.php:
Code:
$hook['post_controller_constructor'][] = array(
                                'function' => 'force_ssl',
                                'filename' => 'ssl.php',
                                'filepath' => 'hooks'
                                );

The function class is implemented in application/hooks/ssl.php as follows:
Code:
function force_ssl()
{
    //if this is a HTTPS request we don't need to test anything
    if (!empty($_SERVER['HTTPS'])) return;

    //get the HTTPS routes from the Config class
    $CI =& get_instance();
    $ssl_routes = $CI->config->ssl_routes();
    if (!is_array($ssl_routes)) return;

    //see if any match the current uri
    $uri = ltrim($CI->uri->uri_string(), '/');
    foreach ($ssl_routes as $route)
    {
        if (preg_match('#^'.$route.'$#', $uri))
        {
            redirect($uri); //will get rewritten by overloaded MY_Config method
        }
    }
}

Why is this a post_controller_constructor hook rather than a pre_controller hook? Because get_instance doesn't seem to work until then. Haven't really looked into the details but if you have stuff in your Controller constructor that is https dependent then you can try to solve that problem.

Final note ... make sure that hooks are enabled in your config.php file :-)


Messages In This Thread
Forcing HTTPS for certain routes using customised Config class and hook - by El Forum - 10-13-2009, 03:37 AM



Theme © iAndrew 2016 - Forum software by © MyBB