戰地連結︰ Home My Flickr NBA.com About

2009年9月16日星期三

Single Sign-on for ASP.NET 3.5 and Legacy ASP

Overview

I got a task on setting Single Sign-on (SSO) for a ASP.NET site and legacy ASP site. For this, Ting Huang has written a very nice post on wwwcoder.com. The idea behind is to create a “bare bone” API for the FormsAuthentication class (without the need of HttpContext), and wrap it in a COM object, which can be used by ASP page. However the post is regarding ASP.NET 1.1 and I am using ASP.NET 3.5, for which the FormsAuthentication class is quite different.

With some more googling I found the way to manually create the authentication ticket, encrypt it and save it to cookie. Using these technique I can make a ASP page create a authenticate cookie which can be consumed and recognized by a ASP.NET web application.

Reference

  1. Creating a Single Sign-on for ASP.NET Application and Legacy ASP Application (Part II)
  2. Single Sign-On for everyone
  3. AppDomainSetup.ConfigurationFile Property

Steps

Write the “bare bone” API for the FormsAuthentication class

using System;

using System.Web.Security;

 

namespace SingleSignon

{

    public class AuthAPI

    {

        public AuthAPI()

        { }

 

        public string FormsCookieName

        {

            get { return FormsAuthentication.FormsCookieName; }

        }

 

        public void Initialize()

        {

            FormsAuthentication.Initialize();

        }

 

        public string SetAuthCookie(

              string userName,

              bool createPersistentCookie)

        {

            try

            {

                FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);

                return "success";

            }

            catch (Exception e)

            {

                return "error: " + e.Message;

            }

        }

 

        public string GetAuthCookieValue(

              string userName,

              bool createPersistentCookie

            )

        {

            try

            {

                FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(userName, createPersistentCookie, 30);

                return FormsAuthentication.Encrypt(newticket);

            }

            catch (Exception e)

            {

                return "error: " + e.StackTrace;

            }

        }

 

        public void GetAuthTicketInfo(

            string cookieValue,

            out string cookiePath,

            out string expireDate,

            out bool expired,

            out bool isPersistent,

            out string issueDate,

            out string userName,

            out string userData,

            out int version

            )

        {

            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookieValue);

            cookiePath = ticket.CookiePath;

            expireDate = ticket.Expiration.ToShortDateString();

            expired = ticket.Expired;

            isPersistent = ticket.IsPersistent;

            issueDate = ticket.IssueDate.ToShortDateString();

            userName = ticket.Name;

            userData = ticket.UserData;

            version = ticket.Version;

        }

    }

}

Setup web.config file for Forms Authentication

<authentication mode="Forms">

  <forms name="DEMO" loginUrl="login.aspx" protection="All" timeout="30" path="/" />

</authentication>

<authorization>

  <deny users="?" />

</authorization>

<machineKey validationKey='9804…CB69' decryptionKey='FEA…D648' validation='SHA1'/>

Be reminded to set the "machineKey" for the ASP.NET web application and share it with legacy ASP site.

Add “AuthAPI” assembly to Global Assembly Cache (GAC) and register it as a COM object

The legacy ASP site is going to use the AuthAPI through COM interface, so we should first register the API as a COM object. To do this, we have to:

  1. Sign the assembly
  2. Add the assembly to GAC. I use the “gacutil.exe” tool at
    C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe
  3. Register the assembly as a COM object. Use “regasm” tool at
    C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm

Indeed you can use the "Post-build event command line" in Visual Studio to do these actions every time you rebuild the assembly. Just remember to unregister the COM object and uninstall the assembly from GAC first.

Setup/Create the “dllhost.exe.config” file for the legacy ASP site

You may wonder how the “AuthAPI” load the Forms authentication settings, does it read from the web.config file of the ASP.NET web app? No, actually it does not have any linkage to the web app. So how can we know which config file is the AuthAPI looking for? We can get the file path from “AppDomain.CurrentDomain.SetupInformation.ConfigurationFile”, and it turns out to be a config file at “C:\WINDOWS\system32\dllhost.exe.config”. It make sense as “dllhost.exe” is the “DCOM DLL Host Process”.

OK so we have to edit this “dllhost.exe.config” file to configure the FormsAuthentication component of “AuthAPI”. You may not see this file in “C:\WINDOWS\system32\”, so just create it and put the Forms authentication configuration in it.

Sample of “dllhost.exe.config” file

<configuration>

  <configSections>

    <sectionGroup name="system.web.extensions" type="System.Web...">

      <!-- Copy all settings from web.config file -->

    </sectionGroup>

  </configSections>

  <system.web>

    <!-- Copy Forms Authentication settings and Machine key from web.config file -->

  </system.web>

</configuration>

Use “AuthAPI” in legacy ASP site to create a Cookie with valid authentication ticket

Now we have the COM object setup and configured, we can use it in our legacy ASP site. The following code shows the function of creating a authentication ticket and putting it into a Cookie. If the ASP.NET web app and the legacy ASP site share the same domain. The Cookie will be shared between them and the Single Sign-on is achieved!

<body>
 
    <%          
            Set ulogin = Server.CreateObject("SingleSignon.AuthAPI")
            userName = "TestUser"
            Call ulogin.Initialize()
            response.write("FormsCookieName: " + ulogin.FormsCookieName() + "<br>")
            
            authCookieValue = ulogin.GetAuthCookieValue(userName, true)
            response.write("AuthCookieValue: " + authCookieValue)
            
            Call AddCookie ( ulogin.FormsCookieName(), "", authCookieValue, 30, "")
            set ulogin = nothing
    %>
 
</body>

沒有留言: