Click here to Skip to main content
Licence 
First Posted 16 Jun 2004
Views 47,509
Bookmarked 39 times

Quick and easy user-level security checks - Part 3

By | 16 Jun 2004 | Article
Demonstrates how to use the security library in a web application

Introduction

My first article in this series presented a library for managing user-level credentials. [^] The next article talked about the internals of that library, and demonstrated how to extend things by building a data access library for use with SQL-Server. [^] This article will demonstrate how to go about using the security library in a typical ASP.NET web application.

Using the code

For this article I created a simple web application that contains a login page and a default page. The login page is shown in the screen shot above. The code for the login page is shown here:

public class LoginPage : System.Web.UI.Page
{

  // ******************************************************************
  // Attributes.
  // ******************************************************************

  protected System.Web.UI.HtmlControls.HtmlInputText m_userName;
  protected System.Web.UI.HtmlControls.HtmlInputCheckBox m_checkboxRemember;
  protected System.Web.UI.HtmlControls.HtmlInputButton m_buttonSubmit;
  protected System.Web.UI.WebControls.Label m_labelMessage;
  protected System.Web.UI.HtmlControls.HtmlInputText m_password;

  // ******************************************************************
  // Page event handlers.
  // ******************************************************************

  private void Page_Load(object sender, System.EventArgs e)
  {
    m_labelMessage.Text = "";
  } // End Page_Load()

  // ******************************************************************
  // Button event handlers.
  // ******************************************************************

  private void m_buttonSubmit_ServerClick(object sender, System.EventArgs e)
  {

    // Use the security manager to authenticate the user.
    if (!SecurityManager.Authenticate(m_userName.Value, m_password.Value))
    {

      m_labelMessage.Text = "failed to authenticate!";

      return;

    } // End if we failed to authenticate.

    // Use the FormsAuthenticattion classe to set the auth cookie.
    FormsAuthentication.SetAuthCookie(
      m_userName.Value, 
      m_checkboxRemember.Checked
      );
      
    // Go back to wherever we came from...
    FormsAuthentication.RedirectFromLoginPage(
      m_userName.Value, 
      m_checkboxRemember.Checked
      );
        
  } // End m_buttonSubmit_ServerClick()

  // ******************************************************************    
  // Private methods.
  // ******************************************************************

  private void _DoNothing() { }

} // End class

Clicking the "login" button causes the m_buttonSubmit_ServerClick handler to be called. As you can see, the method uses the SecurityManager class to authenticate whatever credentials are supplied by the user. Assuming the credentials pass, the FormsAuthentication class is then used to set the authentication cookie and redirect the user to the default page.

Once the user is authenticated the next step is to create a new CustomPrincipal object and associate it with the user. There are several different ways to go about doing that, but what I usually do is populate the Application_AuthenticateRequest event handler, in the Global.asax file, with code like this:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{

  // If the request is not authenticated then do nothing.
  if (!Request.IsAuthenticated)
    return;

  // Otherwise, create a custom principal and identity object and
  //   use them to fill in the user credentials.
  Context.User = new CustomPrincipal(
    new CustomIdentity(Context.User.Identity.Name
    )
  );
                
} // End Application_AuthenticateRequest()

At this point all the hard work is done. The user is authenticated, the current HttpContext contains an instance of our CustomPrincipal and CustomIdentity objects. All that is left to do is demonstrate that we have accomplished something. For that, I added some code to the default page that dumps various user related properties to the page at runtime. The screen shot (after authentication) looks like this:

The code for the default page looks like this:

public class DefaultPage : System.Web.UI.Page
{

  // ******************************************************************
  // Page event handlers.
  // ******************************************************************

  private void Page_Load(object sender, System.EventArgs e)
  {
    
    // Should we go to the login page?
    if (!Context.User.Identity.IsAuthenticated)
      Response.Redirect("login.aspx");
    
    // Recover our custom principal and identity objects.
    CustomPrincipal cp = (CustomPrincipal)Context.User;
    CustomIdentity ci = (CustomIdentity)cp.Identity;

    // Print some user related stuff.
    Response.Write("<h2>Identity Properties:</h2>");
    Response.Write("<p><b>AuthenticationType:</b> " 
        + ci.AuthenticationType + "</p>");      
    Response.Write("<p><b>CreateDate:</b> " + ci.CreateDate.ToString() + "</p>");
    Response.Write("<p><b>IsAuthenticated:</b> " + ci.IsAuthenticated + "</p>");
    Response.Write("<p><b>LastLogin:</b> " + ci.LastLogin.ToString() + "</p>");
    Response.Write("<p><b>Name:</b> " + ci.Name + "</p>");
    Response.Write("<p><b>UserID:</b> " + ci.UserID + "</p>");
    
    Response.Write("<hr>");
                                
    // Print the related roles.
    DataTable dt = UserRoleManager.FindByUser(ci.UserID).Tables[0];

    Response.Write("<h2>User Roles:</h2>");

    foreach (DataRow row in dt.Rows)
      Response.Write((string)row["role_name"] + "<br>");

    // Print the effective rights.
    string[] rights = SecurityManager.EffectiveRights(ci.UserID);

    Response.Write("<h2>Effective Rights:</h2>");

    foreach (string right in rights)
      Response.Write(right + "<br>");

    Response.Write("<hr>");

    Response.Write("<h2>Principal Properties:</h2>");

    // Get the entire collection of roles.
    dt = RoleManager.FindAll().Tables[0];

    // Loop and test using the principal object.
    foreach (DataRow row in dt.Rows)
    {

      string roleName = (string)row["role_name"];

      if (cp.IsInRole(roleName))
        Response.Write("<b>" + roleName + ": IsInRole = true</b><br>");
      else
        Response.Write("<b>" + roleName + ": IsInRole = false</b><br>");

    } // End for every role in the system.

    // Get the entire collection of rights.
    dt = RightManager.FindAll().Tables[0];

    // Loop and test using the principal object.
    foreach (DataRow row in dt.Rows)
    {

      string rightName = (string)row["right_name"];

      if (cp.IsAuthorized(rightName))
        Response.Write("<b>" + rightName + ": IsAuthorized = true</b><br>");
      else
        Response.Write("<b>" + rightName + ": IsAuthorized = false</b><br>");

    } // End for every right in the system.

  } // End Page_Load()

} // End class DefaultPage

The Page_Load method first checks to verify that the user is authenticated. If not, the user is redirected to the login page.

After that check, the CustomPrincipal and CustomIdentity objects are recovered from the current HttpContext and are used to populate the GUI. I also dumped the list of related user roles and effective rights - just to give the demonstration some additional umph.

Configuration

The security library requires some changes to the Web.config file. These changes are needed so that the library can decide how to access its backend database. The following code was added to the Web.config file:

<!-- This section notifies .NET about the custom handler 
  for the CG.Security.Data group. -->
<configSections>
  <sectionGroup name="CG.Security.Data">
    <section name="runtimeSetup" 
type="CG.Security.Data.Configuration.DataSettingsHandler, CG.Security" />
  </sectionGroup>
</configSections>

<!-- This section configures the CG.Security library -->
<CG.Security.Data>
  <runtimeSetup defaultSection="Access">
    <installedAssembly>
      <add 
        sectionName="Access" 
        targetAssembly="CG.Security"
        targetNamespace="CG.Security.Data.Access"
        connectionString="Provider=Microsoft.Jet.OLEDB.4.0;
         User ID=Admin;Data Source=c:\temp\security.mdb;
         Mode=Share Deny None;Extended Properties="";Jet OLEDB:System 
         database="";Jet OLEDB:Registry Path="";Jet OLEDB:Engine Type=5;
         Jet OLEDB:Database Locking Mode=1;
         Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk 
         Transactions=1;Jet OLEDB:Create System Database=False;
         Jet OLEDB:Encrypt Database=False;
         Jet OLEDB:Don't Copy Locale on Compact=False;
         Jet OLEDB:Compact Without Replica Repair=False;
         Jet OLEDB:SFP=False"
      />      
    </installedAssembly>
  </runtimeSetup>
</CG.Security.Data>

The configuration section is used by the security library to locate the proper set of data classes to load at runtime. (since the library can be used with a variety of databases). The section also contains the ADO.NET connection string for the library.

IMPORTANT NOTE: you will need to modify the connection string to point to wherever you decided to copy this project on your computer. For instance, if you copy this project to your c:Inetpub/wwwroot/securitydemo folder then you should put the path: 'c:\Inetpub\wwwroot\securitydemo\' before the 'security.mdb' portion of the connection string.

If you don't do this then the library will not be able to locate the database file. Remember this step - it's important!!!

Conclusion

Of course, you can always handle user authentication and authorization the old fashioned way, but after you write that same block of code for the umpteenth time you may start to understand why I created the security library in the first place. Keep in mind that the library will also work with thick-client applications.

I hope you will enjoy the library - no matter how you decide to use it.

Have fun! :o)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Martin Cook

Web Developer

United States United States

Member

I am a C# developer specializing in creating object-oriented software for Microsoft Windows. When I am not programming I enjoy reading, playing the guitar/piano, running, watching New York Ranger hockey, designing and building wacky electronic devices, and of course enjoying good times with my wife and children.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralWebservices PinmemberSDSimonsen23:43 20 Jun '05  
QuestionConvert it to SqlServer? PinmemberZordo4:20 8 Jun '05  
AnswerRe: Convert it to SqlServer? PinmemberMartin Cook5:54 8 Jun '05  
Questionhow can we redirect the auhonticated user Pinmemberwail A.Rahamn11:29 26 May '05  
GeneralYou did a Great Work Pinmemberwingi1:52 15 Jan '05  
GeneralRe: You did a Great Work PinmemberMartin Cook4:40 17 Jan '05  
GeneralERP Pinmemberleopq6:23 25 Jun '04  
Generalgreat article and class Pinmemberzjian20:53 17 Jun '04  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120517.1 | Last Updated 17 Jun 2004
Article Copyright 2004 by Martin Cook
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid