Click here to Skip to main content
15,868,340 members
Articles / Web Development / ASP.NET
Article

Using a Custom Base Class for Security and Session Management

Rate me:
Please Sign up or sign in to vote.
3.80/5 (6 votes)
21 Jun 2008CPOL2 min read 49.7K   37   16
Using a custom base class for security and Session management.

Introduction

One of the requirements of the project I work on is that, "the admin should be able to mark a user as inactive, so the user would not be able to access the application anymore even if he is currently logged in". First, I thought that this would not be possible, but later on, I found this graat article by Robert Boedigheimer, which suggested the use of a custom base class, which brought to me an entire new horizon on how to tackle the two most important parts of writing .NET web based applications - security and memory management.

Using the code

Derive your page from the base class. In the code-behind file, use:

C#
public partial class MyImportantPage : CustomBasePage

Thus, your pages will have all the properties of the System.Web.UI.Page class plus those you have defined in your base class. The second advantage of this class is Session management. Let us assume that you have 1 GB of RAM on your Web server. What would happen if you retrieve a couple of datasets per page (1 MB each)? Your Session expiration time is 30 minutes and the number of on-line users is close to 600 ... Well, with those simulated numbers, with the default settings, the IIS will perform a memory cleaning and throw all the users out ...

In reality, most of the variables set as Session variables do not need to be global, but are page-specific... Somebody might argue to use ViewState for those variables - well, watch the performance after setting a dataset with a 1 MB size ... the ViewState should only be used for small variables. Another option is to use the Cache ... My experience is that the Cache is unreliable - you cannot be 100% sure what the client browser will do to your important objects stored, yet your code rules the server ...

When you would like to set a variable which is to be used globally (e.g., the variable will persist through the time of the current session of the user, even if the user leaves the current page), set the variable like so:

C#
string StrGlobalVariable = Session [ "global.StrGlobalVariable" ];

When you would like to use a variable only in the scope of the page (e.g., the variable will be deleted once the user requests a different page), set the variable as follows:

C#
string StrPageVariable = Session [ base.PageName + ".PageVariable" ] ;

So, here is the code (read the comments carefully):

C#
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Collections.Generic;
using System.Text;

//SOURCE:http://aspalliance.com/63
//AUTHOR:Robert Boedigheimer
//page life cycle http://msdn.microsoft.com/en-us/library/ms178472.aspx

public class CustomBasePage : System.Web.UI.Page
{
 private string pageName;
#region Properties

    /// <summary>
    /// Each page should "know" its name
    /// </summary>
    public string PageName
    {
        get
        {
            return System.IO.Path.GetFileNameWithoutExtension (
                   System.Web.HttpContext.Current.Request.Url.AbsolutePath );
        } //eof get
        set { pageName = value; } //eof set
    } //eof property MyPageName

    #endregion //Properties


  public CustomBasePage ()
  {
  }

  override protected void OnInit ( EventArgs e )
  {
   base.OnInit ( e );
   #region Introduction
   string msg;
   string baseDir;
   string pageName;
  #endregion //Introduction


 #region Instantiation
  pageName = System.IO.Path.GetFileNameWithoutExtension (
             System.Web.HttpContext.Current.Request.Url.AbsolutePath );

//Utils.Debugger.WriteIf ( pageName + "            OnInit --- Start " );
//ucomment this to debug each page after the login
        //Utils.Debugger.DebugPage ( this.Page );

        
        if (HttpContext.Current.Session != null)
        {

            //Tested and the IsNewSession is more advanced then simply checking if 
            // a cookie is present, it does take into account a session timeout, because 
            // I tested a timeout and it did show as a new session
            if (HttpContext.Current.Session.IsNewSession)
            {
                // If it says it is a new session,
                // but an existing cookie exists, then it must 
                // have timed out (can't use the cookie
                // collection because even on first 
                // request it already contains the cookie (request and response
                // seem to share the collection)
                string szCookieHeader = Request.Headers [ "Cookie" ];
                if (( null != szCookieHeader ) &&
                ( szCookieHeader.IndexOf ( "ASP.NET_SessionId" ) >= 0 ))
                {
                    HttpContext.Current.Session [ "global.msg" ] = 
                        " No action has been performed in " + 
                        "the last 15 minutes, please login again";
                    Response.Redirect ( "~/login.aspx" );
                }
            } //eof if (HttpContext.Current.Session.IsNewSession)
        } //eof is Session is not null 



        //comm - if this is not the login page AND 
        if (Session [ "global.LOGINISOK" ] == null || 
            System.Convert.ToString ( 
            Session [ "global.LOGINISOK" ] ).Equals ( "LOGINISOK" ) == false)
        {
            Utils.Debugger.WriteIf ( Session [ "global.Domain_Name" ] + 
            " redirecting to login page !!!: \n" );
            Session [ "global.msg" ]  = 
            "Welcome to the My Application name ! Please, login first ";
            Response.Redirect ( "~/login.aspx" );

        }
        else   //comm --   
        {
            //comm - clear all session variables , 
            //but the global ones and the ones belonging to this page
    ClearAllNonGlobalSessionsButMine ();
        }
    } //eof OnInit



    /// <summary>
    /// This method relies on the convention practice that the Session keys
    /// would be named as follows:
    /// pageName.variableName - if the Session variable is page specific and 
    /// global.variableName - if the Session variable should be trully global
    /// </summary>
    private void ClearAllNonGlobalSessionsButMine ()
    {
        string fileNameNoExt = System.IO.Path.GetFileNameWithoutExtension (
                 System.Web.HttpContext.Current.Request.Url.AbsolutePath );

        for (int i = 0; i< HttpContext.Current.Session.Count; i++)
        {
            string keyValue = HttpContext.Current.Session.Keys [ i ];
            if (keyValue.Contains ( fileNameNoExt ) || keyValue.Contains ( "global" ))
            { ; }        //do nothing = preserve the value in the session 
            else
            {
              //answers the question: what session
              //variables are deleted during page change
              HttpContext.Current.Session [ HttpContext.Current.Session.Keys [ i ] ] = null;
            } //eof else if it is global or belongs to current page
        } //eof loop 

    } //eof public static void ClearAllButCurrent (string pageName )  

public void ClearAllNonGlobalSessionsAndMine ()
{
string fileNameNoExt = System.IO.Path.GetFileNameWithoutExtension (
                                System.Web.HttpContext.Current.Request.Url.AbsolutePath );

  for (int i = 0; i< HttpContext.Current.Session.Count; i++)
  {
  string keyValue = HttpContext.Current.Session.Keys [ i ];
  if (keyValue.Contains ( "global" ))
  { ; }  //do nothing = preserve the value in the session 
  else
  {
  //answers the question: what session variables are deleted during page change
  HttpContext.Current.Session [ HttpContext.Current.Session.Keys [ i ] ] = null;
} //eof else if it is global or belongs to current page
} //eof loop 

} //eof public static void ClearAllButCurrent (string pageName )  


} //eof BasePageClass

I almost noticed, you wandered what the DebugPage method is. Here it is:

C#
using System;
using System.Text.RegularExpressions;
using System.Data;
using System.Web.UI.WebControls;

namespace Utils
    {
    /// <summary>
    /// Summary description for Utils.Debugger
    /// </summary>
    public class Debugger

public static void DebugPage ( System.Web.UI.Page page )
{

string baseDirLocal = System.Web.HttpContext.Current.Server.MapPath ( "~" );
Regex Remover = new Regex ( @"^.*(\\|\/)(.*)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled );
string bareDir = Remover.Replace (  baseDirLocal , "$2" );


string strToRemoveAtEnd= System.Web.HttpContext.Current.Request.Url.AbsolutePath;
char [] charsToRemoveAtEnd = strToRemoveAtEnd.ToCharArray ();
string baseDir = System.Web.HttpContext.Current.Request.Url.AbsoluteUri;
baseDir = baseDir.TrimEnd ( charsToRemoveAtEnd );
baseDir = baseDir + "/" + bareDir + "/";



WriteIf("The basedir of the project locally is " +  
        "HttpContext.Current.Server.MapPath(\"~/\");" +
        System.Web.HttpContext.Current.Server.MapPath ( "~" ) );

WriteIf ( "The basedir of the project locally is " + 
          "AppDomain.CurrentDomain.BaseDirectory;" + 
          AppDomain.CurrentDomain.BaseDirectory);

WriteIf ( "The virtual path of this page " + 
          "( System.Web.HttpContext.Current.Request.PathInfo) is " + 
          System.Web.HttpContext.Current.Request.PathInfo ) ;

WriteIf ( "System.IO.Path.GetFileName (" + 
          " System.Web.HttpContext.Current.Request.Url.AbsolutePath ) -- " + 
          System.IO.Path.GetFileName ( 
          System.Web.HttpContext.Current.Request.Url.AbsolutePath ) );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.AbsolutePath -- " + 
          System.Web.HttpContext.Current.Request.Url.AbsolutePath );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.AbsoluteUri -- " + 
          System.Web.HttpContext.Current.Request.Url.AbsoluteUri );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.DnsSafeHost -- " + 
          System.Web.HttpContext.Current.Request.Url.DnsSafeHost );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.Fragment -- " + 
          System.Web.HttpContext.Current.Request.Url.Fragment );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.Host -- " + 
          System.Web.HttpContext.Current.Request.Url.Host ) ; 
WriteIf ( "System.Web.HttpContext.Current.Request.Url.HostNameType -- " + 
          System.Web.HttpContext.Current.Request.Url.HostNameType ) ;
WriteIf ( "System.Web.HttpContext.Current.Request.Url.LocalPath -- " + 
          System.Web.HttpContext.Current.Request.Url.LocalPath ) ;
WriteIf ( "System.Web.HttpContext.Current.Request.Url.OriginalString -- " + 
          System.Web.HttpContext.Current.Request.Url.OriginalString ) ;
WriteIf ( "System.Web.HttpContext.Current.Request.Url.PathAndQuery -- " + 
          System.Web.HttpContext.Current.Request.Url.PathAndQuery ) ;
WriteIf ( "System.Web.HttpContext.Current.Request.Url.Scheme -- " + 
          System.Web.HttpContext.Current.Request.Url.Scheme ) ;
WriteIf ( "System.Web.HttpContext.Current.Request.Url.Segments.ToString () -- " + 
          System.Web.HttpContext.Current.Request.Url.Segments.ToString () );
WriteIf ( "System.Web.HttpContext.Current.Request.Url.UserInfo -- " + 
          System.Web.HttpContext.Current.Request.Url.UserInfo );
WriteIf ( "System.Web.HttpContext.Current.Request.ServerVariables[ \"HTTP_COOKIE\" ] is " + 
          System.Web.HttpContext.Current.Request.ServerVariables [ "HTTP_COOKIE" ] );

} //eof DebugPage

        public static void WriteIf ( string msg )
        {
  System.Diagnostics.Debug.WriteIf ( System.Convert.ToBoolean
    ( System.Configuration.ConfigurationSettings.AppSettings [ "Debugging" ] ) ,
  DateTime.Now.ToString ( "yyyy:MM:dd -- hh:mm:ss.fff --- " ) + msg + "\n" );
        }
} //eof class Debugger 
} //eof namespace  Utils

Points of interest

If you have carefully read the code, now you know where to put your code for implementing the requirement I presented above. If you are still having difficulties figuring out, put a comment below, and I will spare more time for writing an article about it ...

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer Oxit Oy
Finland Finland
I work in OXIT - a small IT consulting company, which has participated in the building of the most sophisticated IT Systems for several big Finnish and international companies (including Fortune 500 members) and continues to provide highly sophisticated IT Solutions to its customers.

I enjoy designing and implementing software or small scripts in different programming languages.

I am fascinated by the magic of software, which has the power to change the world.

Comments and Discussions

 
GeneralThank You (and one small request) Pin
forCryingOutLoud21-May-09 11:10
forCryingOutLoud21-May-09 11:10 
GeneralRe: Thank You (and one small request) Pin
yordan_georgiev21-May-09 14:30
yordan_georgiev21-May-09 14:30 
GeneralMethod for testing for timed out session is incomplete Pin
ccady19-Nov-08 9:55
ccady19-Nov-08 9:55 
GeneralRe: Method for testing for timed out session is incomplete Pin
yordan_georgiev20-Nov-08 1:31
yordan_georgiev20-Nov-08 1:31 
Generalmultiple tabs Pin
dbwinger23-Jun-08 2:20
dbwinger23-Jun-08 2:20 
GeneralRe: multiple tabs Pin
Mike Ellison23-Jun-08 3:07
Mike Ellison23-Jun-08 3:07 
dbwinger wrote:
What if a user navigates to the same page on two (or more) tabs in the same browser. The user shares the same session on both pages. If the user navigates away to a different page (still in your application) on one of the tabs, the session values specific to the first page will be cleared, and the other tab which is still on that page will not have the session values it expects to have.


Okay, what about an architecture that uses an attribute to mark pages, something like UsesSessionVariable, like this:
[UsesSessionVariable("SomeSessionName")]
partial class MyPage
{
  ...
}

So if two pages share the same variables, they could be marked with the same attribute?
GeneralRe: multiple tabs Pin
dbwinger23-Jun-08 3:12
dbwinger23-Jun-08 3:12 
GeneralRe: multiple tabs Pin
yordan_georgiev25-Jun-08 20:52
yordan_georgiev25-Jun-08 20:52 
GeneralRe: multiple tabs Pin
dbwinger26-Jun-08 2:13
dbwinger26-Jun-08 2:13 
GeneralRe: multiple tabs [modified] Pin
yordan_georgiev23-Jun-08 7:58
yordan_georgiev23-Jun-08 7:58 
GeneralRe: multiple tabs Pin
star6522569223-Jun-08 23:57
star6522569223-Jun-08 23:57 
GeneralA different type of though. Pin
Rajib Ahmed22-Jun-08 6:28
Rajib Ahmed22-Jun-08 6:28 
QuestionHow about an HttpModule? Pin
Mike Ellison22-Jun-08 4:56
Mike Ellison22-Jun-08 4:56 
AnswerRe: How about an HttpModule? [modified] Pin
yordan_georgiev22-Jun-08 19:59
yordan_georgiev22-Jun-08 19:59 
GeneralRe: How about an HttpModule? Pin
Mike Ellison23-Jun-08 3:11
Mike Ellison23-Jun-08 3:11 
GeneralRe: How about an HttpModule? Pin
yordan_georgiev23-Jun-08 20:29
yordan_georgiev23-Jun-08 20:29 

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

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