戰地連結︰ 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>

2009年9月9日星期三

Disabling Time Synchronization under Virtual PC 2007

Overview

VPC with Virtual Machine Additions installed will automatically synchronize its time with host. Virtual PC Guy tells us how to disable it in his post.

Reference

Disabling Time Synchronization under Virtual PC 2007

Steps

  1. Shut down the VPC (I found that cannot use “Save state”)
  2. Open the .VMC file with notepad and add the "components" element in it:

    <integration>

      <microsoft>

        <mouse>

          <allow type="boolean">true</allow>

        </mouse>

        <!--Add the "components" element-->

        <components>

          <host_time_sync>

            <enabled type="boolean">false</enabled>

          </host_time_sync>

        </components>

  3. Save the .VMC file and restart VPC, done!

Associate XSD to XML file to enable IntelliSense

Overview

We .Net developers use IntelliSense extensively in our everyone work. I am happy using IntelliSense for XML files like web.config file. Then one day I come across a XML file with schema not available out-of-the-box from VS (think about NHibernate hbm files). How can I use IntelliSense with it?

Reference

XML Schemas dialog in Visual Studio 2008

Steps

To use schema(s) for a XML file

  1. Open a XML file in Visual Studio 2008
  2. In menu bar, select "XML" => "Schemas…"
  3. In the "XML Schemas" windows, select "Use this Schema" in the "Use" column next to your desired schema
  4. Click OK, done!

The "XML Schemas" windows will search for schema files (.XSD) in your project or in folder %VS 2008 install folder%\xml\Schemas. You can also manually add schemas files using the "Add…" button

Introduction to ASP.NET Forms Authentication

In ASP.NET, forms authentication means that users authenticate themselves using a Web form. This feature is provided by the HTTP module FormsAuthenticationModule. Setting up forms authentication in ASP.NET is quite simple and is presented in a post at WindowsDevCenter.com.

Reference

  1. ASP.NET Forms Authentication - Part 1
  2. Explained: Forms Authentication in ASP.NET 2.0 (MSDN)
  3. FormsAuthentication Class (MSDN)

Steps

Enable anonymous access in IIS.

Anonymous access is enabled by default. If not, enable it manually for the web application.

Modify web.config file to allow Forms Authentication

To allow Forms Authentication, first we have to add the "authentication" element under "system.web" in web.config.

  1. In the "authentication" element, set the "mode" attribute to “Forms” to specify Forms Authentication.
  2. Add a "forms" element under "authentication" element to specify configuration settings for Forms Authentication
  3. Add a "authorization" element under "authentication" element to deny all anonymous users and redirect them to login page.

Code Snippet (web.config):

<configuration>

  <system.web>

    <authentication mode="Forms">

      <forms name="DEMO"

            loginUrl="login.aspx"

            protection="All"

            timeout="30"

            path="/" />

    </authentication>

    <authorization>

      <deny users="?" />

    </authorization>

  </system.web>

</configuration>

Create the login page

The login page is where denied users will be redirected to. It is referenced by "loginUrl" attribute in "forms" element. As shown by the above code snippet, our login page will be “login.aspx”.

Code Snippet (login.aspx.cs)

protected void Login_Click(Object sender, EventArgs E)

{

    if ((UserName.Value == "username") &&

        (UserPass.Value == "password"))

    {

        FormsAuthentication.RedirectFromLoginPage(UserName.Value, PersistCookie.Checked);

    }

    else

    {

        lblResults.Text = "Invalid Credentials: Please try again";

    }

}

The above code snippet is the onClick event of the login button. When it is fired, it will:

  1. Validate the user credentials entered. In this case the validation logic is completely provided by us and only allow one user to login.
  2. If the user is valid, FormsAuthentication.RedirectFromLoginPage() is called to authenticate the user and redirect back to the page he/she wants to visit.
  3. Else a warning message is shown.

Configure user credentials in web.config

Usually we will store the user credentials in a Database. However for easy implementation (or for testing purpose) we can also set user credentials in web.config file.

By adding "credentials" element under "forms" element, we can add user credentials in username/password pair format.

<forms>

    <credentials passwordFormat="Clear">

      <user name="user1" password="password1"/>

      <user name="user2" password="password2"/>

      <user name="user3" password="password3"/>

    </credentials>

</forms>

Then we can use FormsAuthentication.Authenticate() method to validate user credentials against those stored in web.config.

2009年9月3日星期四

ASP.NET MVC - Get Started

Overview

I just started learning ASP.NET MVC, and would like to list some learning materials links here.

Reference

Official ASP.NET MVC Tutorials - http://www.asp.net/learn/mvc/

Download "Microsoft Web Platform Installer" to get ASP.NET MVC - http://www.microsoft.com/web/downloads/platform.aspx

NerdDinner at CodePlex - http://nerddinner.codeplex.com/

NerdDinner Site - http://www.nerddinner.com/