onsdag den 20. august 2014

Require HTTP / HTTPS for certain pages with only a single processor, a base template and a few simple items.

From time to time, customers would like to define, that a certain page should be viewed using HTTPS, while the rest of the website remains as HTTP.

There are many ways to do this - however, I find this solution to be the most simple, since it doesn't require alot of custom code, and once implemented, it can be reused without having to think about it.

Some items

The first step is to create a few dummy items, that is used to define the states that can be selected in the dropdown box added to the base template.

To do this, go into the /sitecore/System/Modules folder, and create a new folder here called "Require SSL Modes"

Right click this folder, and select "Insert from Template" - now find the "Standard Template" template, and insert an item from this template - call the item "Inherit".

Duplicate this item three times, and call the three copies "Leave Default", "Force HTTP" and "Force HTTPS".

Now you should have 4 items in the folder.
The reason for using the "Standard Template" instead of our own template is, that we don't need any fields on these, so just creating an extra template just for this seems redundant.

A template

The next thing to do, is to create a base template, that has the field used on pages to define if a page should be using this or not.

So go into the /sitecore/Templates folder, and create a template where it makes sense in your structure.

On this template, add a single field, call it "HTTP Mode" - set it's type to Droplist, and define it's source to "/sitecore/System/Modules/Require SSL Modes" - then save the template.

Now make your page templates inherit from this template, so they have the field.

A simple processor

Now we are getting to the fun part - fire up Visual Studio, and create the following C# class:

public class RequireSslProcessor : HttpRequestProcessor
{
    public override void Process(HttpRequestArgs args)
    {
        if (Sitecore.Context.Item == null)
        {
            return;
        }

        if (args == null || args.Context == null)
        {
            return;
        }

        // First we set the current item as our starting point.
        Item currentItem = Sitecore.Context.Item;

        // We keep going until we haven't found anything.
        while (currentItem != null)
        {
            // Also, the current page need to inherit our field.
            Field httpModeField = currentItem.Fields["HTTP Mode"];
            if (httpModeField == null)
            {
                return;
            }

            Uri url = args.Context.Request.Url;
            Uri newUrl;

            // If one of the force modes, and the other one is present, we rewrite the url scheme.
            switch (httpModeField.Value)
            {
                case "Force HTTP":
                    if (url.Scheme.Equals("http", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return;
                    }

                    newUrl = RewriteUrlScheme(url, "http");

                    break;
                case "Force HTTPS":
                    if (url.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return;
                    }

                    newUrl = RewriteUrlScheme(url, "https");

                    break;
                case "Inherit":
                    currentItem = currentItem.Parent;
                    continue;
                default:
                    return;
            }

            // And then we redirect, and stops the pipeline.
            args.Context.Response.Clear();
            args.Context.Response.Headers.Add("Location", newUrl.ToString());
            args.Context.Response.Status = "301 - Moved Permanently";
            args.Context.Response.StatusCode = 301;
            args.Context.Response.End();

            args.AbortPipeline();
            return;
        }
    }

    private static Uri RewriteUrlScheme(Uri url, string newScheme)
    {
        // First we change the scheme.
        UriBuilder newUriBuilder = new UriBuilder(url)
        {
            Scheme = newScheme
        };

        // Then we change the port, since changing the scheme doesn't change the port for some reason.
        switch (newScheme)
        {
            case "http":
                newUriBuilder.Port = 80;
                break;
            case "https":
                newUriBuilder.Port = 443;
                break;
        }

        return newUriBuilder.Uri;
    }
}

Save the class, compile the project and make sure the DLL file is in the bin folder of the Sitecore installation.

An include file to wrap it up

The last thing to do, is to create an include file, so Sitecore knows that it should use the processor.

Open your favorite XML editor and create a new XML file - call it Modules.RequireSSLModes.config .

Enter this into the file, replacing the Namespace, Classname and Assembly parts so they match your project:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor type="Namespace.Classname, Assembly" patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"/>
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

Then save the config file.

To test this, make sure the website is configured to use both HTTP and HTTPS inside IIS.
Then create a few pages from items that inherits this field, and change their setting to different HTTP Mode values, and navigate around between them.

It is really that simple :)

Ingen kommentarer:

Send en kommentar