Combined SSL HttpModule for SharePoint Webs and Pages

Posted by: Clarity Blogs: ASP.NET, on 02 Jan 2009 | View original | Bookmarked: 0 time(s)

I'm currently working on a .com SharePoint site where access to several sub-sites is restricted to logged in users. The team also implemented custom login and registration pages for these sites as layouts pages.

We have a requirement that these sub-sites and the layouts pages both use SSL.

I came across two very helpful blog posts that addressed each scenario individually:

With very little tweaking, each of these handles the particular situation it was designed for very well. However, I basically needed a combination of these two approaches.

If I tried enabling both HttpModules on my SharePoint web application, one scenario would work, but not the other. So I decided to combine both approaches into a single HttpModule:

   1:      public class SSLHttpModule : IHttpModule
   2:      {
   3:          private HttpApplication _application;
   4:   
   5:          private String _baseURLNoSSL;
   6:          private String _baseURLWithSSL;
   7:          private String _sslPagesList;
   8:   
   9:          private List<String> _sslPages = new List<string>();
  10:   
  11:          const String BASEURL_NOSSL = "BaseUrlNoSSL";
  12:          const String BASEURL_SSL = "BaseUrlSSL";
  13:          const String SITE_REQUIRES_SSL_PROPERTY = "requiressl";
  14:          const String SSL_LAYOUTS_PAGES = "SSLLayoutsPages";

 

   1:   
   2:          public void Init(HttpApplication application)
   3:          {
   4:              if (ConfigurationManager.AppSettings[BASEURL_NOSSL] != null)
   5:                  _baseURLNoSSL = ConfigurationManager.AppSettings[BASEURL_NOSSL];
   6:              else
   7:                  return;
   8:              if (ConfigurationManager.AppSettings[BASEURL_SSL] != null)
   9:                  _baseURLWithSSL = ConfigurationManager.AppSettings[BASEURL_SSL];
  10:              else
  11:                  return;
  12:              if (ConfigurationManager.AppSettings[SSL_LAYOUTS_PAGES] != null)
  13:                  _sslPagesList = ConfigurationManager.AppSettings[SSL_LAYOUTS_PAGES];
  14:              else
  15:                  return;
  16:   
  17:              // Create a List<String> of Layouts pages requiring SSL
  18:              //
  19:              char[] separator; separator = new char[] { ',' };
  20:              string[] pages; pages = _sslPagesList.Split(separator);
  21:              _sslPages.AddRange(pages);
  22:   
  23:              application.PreRequestHandlerExecute += new EventHandler(PreRequest);
  24:              _application = application;
  25:          }

 

There are three application settings (that live in web.config) that make the whole thing work:

  • BaseUrlNoSSL - the url of your site when you're not using SSL
  • BaseUrlSSL - the url of your site when you're using SSL
    • I use an Alternate Access Mapping to do this
  • SSLLayoutsPages - a comma-separated list of layouts pages which should be secured by SSL
    • Nothing's stopping you from modifying this code to make it work with any page, not just layouts pages.

The following listing shows the PreRequest event of the HttpModule. What I'm doing differently here is handling the case for layouts pages first, and then checking if the SPWeb needs SSL.

   1:  public void PreRequest(object sender, EventArgs e)
   2:  {
   3:      HttpContext ctx = null;
   4:      SPContext spContext = null;
   5:   
   6:      try
   7:      {
   8:          ctx = HttpContext.Current;
   9:          spContext = SPContext.Current;
  10:          if (spContext != null)
  11:          {
  12:              if (spContext.Web != null)
  13:              {
  14:                  string pathAndQuery = ctx.Request.Url.PathAndQuery;
  15:   
  16:                  if (pathAndQuery.ToLower().Contains("_layouts"))
  17:                  {
  18:                      bool pageRequiresSSL = false;
  19:   
  20:                      foreach (var page in _sslPages)
  21:                      {
  22:                          if (pathAndQuery.ToLower().Contains(page.ToLower()))
  23:                          {
  24:                              pageRequiresSSL = true;
  25:                              break;
  26:                          }
  27:                      }
  28:   
  29:                      if (pageRequiresSSL)
  30:                      {
  31:                          if (HttpContext.Current.Request.Url.Scheme.ToString() == "http")
  32:                          {
  33:                              string url = HttpContext.Current.Request.Url.ToString();
  34:                              url = url.Replace("http://", "https://");
  35:                              HttpContext.Current.Response.Redirect(url);
  36:                          }
  37:                      }
  38:                      else
  39:                      {
  40:                          if (HttpContext.Current.Request.Url.Scheme.ToString() == "https")
  41:                          {
  42:                              string url = HttpContext.Current.Request.Url.ToString();
  43:                              url = url.Replace("https://", "http://");
  44:                              HttpContext.Current.Response.Redirect(url);
  45:                          }
  46:                      }
  47:                  }
  48:                  else
  49:                  {
  50:                      bool siteRequiresSSL = false;
  51:                      if (spContext.Web.Properties.ContainsKey(SITE_REQUIRES_SSL_PROPERTY))
  52:                      {
  53:                          siteRequiresSSL = (spContext.Web.Properties
  54:                              [SITE_REQUIRES_SSL_PROPERTY].ToString()).ToLower()
  55:                              == Boolean.TrueString.ToLower();
  56:                      }
  57:   
  58:                      if (siteRequiresSSL & !ctx.Request.IsSecureConnection)
  59:                      {
  60:                          if (_baseURLWithSSL != null)
  61:                              ctx.Response.Redirect(_baseURLWithSSL + pathAndQuery);
  62:                          else
  63:                              ctx.Response.Redirect(String.Concat("https://", 
  64:                                  ctx.Request.Url.Host, pathAndQuery));
  65:                          return;
  66:                      }
  67:                      if (!siteRequiresSSL & ctx.Request.IsSecureConnection)
  68:                      {
  69:                          if (_baseURLNoSSL != null)
  70:                              ctx.Response.Redirect(_baseURLNoSSL + pathAndQuery);
  71:                          else
  72:                              ctx.Response.Redirect(String.Concat("http://", 
  73:                                  ctx.Request.Url.Host, pathAndQuery));
  74:                          return;
  75:                      }
  76:                  }
  77:              }
  78:          }
  79:   
  80:      }
  81:      catch (Exception) { }
  82:  }

 

Some notes about this:

  • I provisioned the RequireSSL property of each SPWeb as part of our provisioning process in a custom site definition.
  • We provisioned all the required web.config modifications in a feature receiver, definitely a much more elegant way than manually modifying your web.config file

Advertisement
Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.
Category: Sharepoint | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 1821 | Hits: 9

Similar Posts

  • Update to SharePoint SSL Switching HttpModule more
  • Multi-Page Applications in Silverlight more
  • Web.UI Navigation Controls and Web Services more
  • How to Create a WebService Based on a Workflow more
  • Tip/Trick: How to Run a Root / Site with the Local Web Server using VS 2005 SP1 more
  • Recipe: Dynamic Site Layout and Style Personalization with ASP.NET more
  • ASP.NET 1.1 database cache dependency (without triggers) more
  • Consuming Webservices over HTTPS (SSL) more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD