Recipe 8.1 Restricting Access to All Application Pages

     

8.1.1 Problem

You want to restrict access to the pages of your application to authorized users only.

8.1.2 Solution

Change the web.config settings of your application to specify Forms authentication, and then create an .aspx login page to collect user credentials and complete the authentication check.

Modify web.config as follows :

  1. Set the mode attribute of the <authentication> element to Forms .

  2. Add a <forms> child element to the <authentication> element to specify key aspects of the Forms implementation:

      <authentication mode="Forms">  <forms name =".ASPNETCookbook"   loginUrl="Login.aspx"   protection="All"   timeout="30"   path ="/">   </forms>  </authentication>  

  3. Add <deny> and <allow> child elements to the <authorization> element to deny access to anonymous users and allow access to all who have been authenticated:

      <authorization>  <deny users="?" /> <!-- Deny anonymous users -->   <allow users="*" /> <!-- Allow all authenticated users -->  </authorization>  

In the .aspx file for the login page:

  1. Add the fields required to collect the data the application needs to authenticate the user. Most applications require, at a minimum, a user login ID and password, but you can specify whatever your application requires.

  2. Add a Login button.

  3. (Optional) Include a checkbox for users to indicate that they want to be remembered between sessions. (You will need to add some code to the code-behind class to persist the authentication cookie on the client machine.)

In the code-behind class for the login page, use the .NET language of your choice to:

  1. Use the Login button click event to verify the user credentials.

  2. If the user credentials are valid, create a Forms authentication cookie and add it to the cookie collection returned to the browser by calling the SetAuthCookie method of the FormsAuthentication class.

  3. (Optional) Set the Forms authentication cookie to be persisted on the client machine.

  4. Redirect the user to the appropriate application start page using Response.Redirect .

The code we've created to illustrate this solution is shown in Example 8-1 through Example 8-4. Example 8-1 shows the modifications we make to web.config to restrict access to all pages. Example 8-2 shows the .aspx file for the login page. Example 8-3 (VB) and Example 8-4 (C#) show the code-behind class for the login page. Figure 8-1 shows the login page produced by the application.

Figure 8-1. Example login page
figs/ancb_0801.gif

8.1.3 Discussion

ASP.NET runs within the context of IIS and all requests must first pass through IIS, so setting up the security for an ASP.NET application always starts with setting up security in IIS. For this recipe, we do not want IIS to perform any authentication. Therefore, the web site (or virtual directory) must be set up to allow anonymous access. (We won't take you through setting up anonymous access in IIS ”it is relatively easy to do and is well documented in MSDN. Just search for "IIS authentication".)

The first step to restricting access to all pages of an application is to enable ASP.NET security. This is done by setting the mode attribute of the <authentication> element of the web.config file to Forms . (Other options are Windows and Passport .)

The second step is to add a <forms> child element to the <authentication> element in order to specify the details of the Forms implementation. The <forms> element has the following attributes:


name

Defines the name of the HTTP cookie used by ASP.NET to maintain the user authentication information. Care should be taken when naming the cookie. If two applications on the same server use the same cookie name, "cross authentication" could occur.


loginUrl

Defines the page to which ASP.NET will redirect users when they attempt to access pages in your application without being logged in. The login page should provide the fields required to authenticate the user, typically a login ID and password or whatever else your application requires.


protection

Defines the protection method used for the cookie. Possible values are All , None , Encryption , and Validation . Validation specifies that the cookie data will be validated to ensure it was not altered in transit. Encryption specifies that the cookie is encrypted. All specifies that data validation and encryption will be used. None specifies no protection will be provided for the cookie information. The default is All and is highly recommended because it offers the highest level of protection for this authentication cookie.


timeout

Defines the amount of time in minutes before the cookie expires . The value provided here should be at least as long at the timeout for the session. Making the value shorter than the session timeout can result in a user being redirected to the page defined by the loginUrl before the session times out.


path

Defines the path of cookies issued by the application. Be aware that most browsers treat the path as case-sensitive and will not return the cookie for a request that does not match the value provided for the path attribute. The result will be having the users redirected as if they were not logged in. Unless your application requires specifying the path, we recommend that you leave the path as " / ".

Here is an example of the <forms> element and its attributes:

 <authentication mode="Forms">  <forms name=".ASPNETCookbook"   loginUrl="Login.aspx"   protection="All"   timeout="30"   path="/">   </forms>  </authentication> 

The next step is to modify web.config to deny access to all anonymous users and allow access to all users who have been authenticated. This is done by adding <deny> and <allow> child elements to the <authorization> element:

 <authorization>  <deny users="?" /> <! ” Deny anonymous users ”>   <allow users="*" /> <! ” Allow all authenticated users ”>  </authorization> 

Your application login page should provide the fields required to enter the data needed to authenticate the user. This is typically a login ID and password, which you gather via text boxes, but can be whatever your application requires. ASP.NET provides the ability to persist the authentication cookie on the client machine. If your application supports "auto login" from a persistent cookie, you should provide a checkbox for the user to indicate that she wants to be remembered between sessions. In addition, your login page should include a button to initiate the login process after the data has been entered. How we have done this for our application is illustrated in Figure 8-1 and in the .aspx file (Example 8-2).

In the code-behind for the login page, use the login button click event to verify the user credentials. In Example 8-3 and Example 8-4, for example, the database is queried for a user matching the entered login ID and password using a DataCommand and a DataReader . After the DataReader is created, the record pointer is by default positioned before the first record in the reader. By calling the Read method in the If statement, you can check to see if the user credentials are found and read the user credentials from the database at the same time. If the user credentials are found in the database, the Read method will return True . Otherwise, it will return False .

If the user credentials are valid, the Forms authentication cookie needs to be created and added to the cookie collection returned to the browser. This can be done by calling the SetAuthCookie method of the FormsAuthentication class, passing the user name and a Boolean value indicating whether the value should be persisted on the client machine. To persist the authentication cookie and allow the user to access the application on subsequent sessions without logging in, set the second parameter to True . If the second parameter is set to False , the authentication cookie is stored in memory on the client and is destroyed when the session expires or the user closes the browser.

Because the SetAuthCookie method is static, it is not necessary to create a FormAuthentication object to use the method. The SetAuthCookie method is the simplest approach to creating and adding the cookie to the cookie collection, but is not very flexible. For an example of a more flexible approach that allows you to store additional data in the authentication cookie, see Recipe 8.3.


After the application has created the authentication cookie, the user should be redirected to the appropriate start page. When ASP.NET redirects the user to the login page defined in your web.config file, it automatically appends the name of the originally requested page to the redirected URL, as shown next. You can use this information to redirect users to the originally requested page or simply redirect them to a fixed page, as illustrated in Example 8-3 (VB) and Example 8-4 (C#).

 http://localhost/ASPNetBook/Login.aspx?ReturnUrl=Home.aspx 

You must use Response.Redirect to redirect the user to the next page. Response.Redirect returns information to the browser instructing it to redirect to the indicated page. This round trip to the client browser writes the authentication cookie to the browser so it will be returned to the server on subsequent page requests. This cookie is what ASP.NET uses to determine if the user has been authenticated. Using other mechanisms, such as Server.Transfer , will not cause the authentication cookie to be written to the browser, resulting in the user being redirected back to the login page.

ASP.NET provides in a single method call (the RedirectFromLoginPage method of the FormsAuthentication class) the ability to create the authentication cookie, add it to the cookie collection, and redirect the user back the original page. This method sounds like it would save a few lines of code; however, if the user was not redirected to the login page (that is, if her original request was the login page), ASP.NET will redirect her to a page named Default.aspx . If your application does not use Default.aspx as the "home" page, using RedirectFromLoginPage creates more problems than it solves .


No other code is required. In other words, by using the simple code just described (without adding code to each page, as is required in classic ASP), access to all of the pages in your application is restricted to logged-in users.

So how does ASP.NET do this so easily? When your application is configured to use Forms authentication, ASP.NET looks for the cookie defined by the name attribute of the <forms> element in web.config for every page requested from your application. If the cookie does not exist, ASP.NET assumes the user is not logged in and redirects the user to the page defined by the loginUrl attribute. If the cookie does exist, ASP.NET assumes the user is authenticated and passes the request on to the requesting page. In addition, when the cookie exists, ASP.NET creates a user principal object with the information found in the authentication cookie. The user principal object (or principal object for short) represents the security context under which code is running. This information is available to your application by accessing the User object in the current context. To get the user name you added to the authentication cookie, use the line of code shown here:

 
figs/vbicon.gif
 userName = Context.User.Identity.Name 
figs/csharpicon.gif
 userName = Context.User.Identity.Name; 

Applications that provide the ability to log in ”particularly those that provide the ability to persist the information on the client machine to eliminate the need to log in on subsequent visits ”should provide the ability to log out. In this context, logging out simply destroys the authentication cookie on the client machine, which then requires the user to log in again to gain access to your application. This can be accomplished with the line of code shown here:

 
figs/vbicon.gif
 FormsAuthentication.SignOut( ) 
figs/csharpicon.gif
 FormsAuthentication.SignOut( ); 

ASP.NET can provide security only for files that are mapped to the ASP.NET ISAPI DLL. By default, these are files with the extensions .asax , .ascx , .ashx , .asmx , .aspx , .axd , . vsdisco , .rem , .soap , .config , .cs , .csproj , .vb , .vbproj , .webinfo , .licx , and .resx and resources. Any other file types ”such as .gif , .jpg , .txt , .js , and the like ”are not protected by ASP.NET security. If access to these file types must also be restricted, they will have to be added to the list of file types processed by the ASP.NET ISAPI DLL. This can be done in the application configuration section of the IIS properties of your application. Requiring these file types to be processed by ASP.NET will affect performance of your application due to the extra processing required for the images, text files, JavaScript, and the like.


8.1.4 See Also

Recipe 8.3; MSDN documentation for IIS setup (search for "IIS authentication")

Example 8-1. Changes to web.config to restrict access to all pages
 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> ..  <authentication mode="Forms">   <forms name=".ASPNETCookbookVB"   loginUrl="Login.aspx"   protection="All"   timeout="30"   path="/">   </forms>   </authentication>   <authorization>   <deny users="?" /> <!-- Deny anonymous user -->   <allow users="*" /> <!-- Allow all authenticated users -->   </authorization>  .. </system.web> </configuration> 

Example 8-2. Login page (.aspx)
 <%@ Page Language="vb" AutoEventWireup="false" Codebehind="Login.aspx.vb" Inherits="ASPNetCookbook.VBSecurity81.Login"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Login</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmSecurity" method="post" runat ="server"> <table width="100%" cellpadding ="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center" class="PageHeading"> Block Access To All Pages (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table> <tr> <td class="LabelText">Login ID: </td> <td>  <asp:TextBox ID="txtLoginID" Runat="server"   CssClass="LabelText" />  </td> </tr> <tr> <td class="LabelText">Password: </td> <td>  <asp:TextBox ID="txtPassword" Runat="server"   CssClass="LabelText" TextMode="Password" />  </td> </tr> <tr> <td colspan="2" align="center">  <asp:CheckBox ID="chkRememberMe" Runat="server"   CssClass="LabelText" Text="Remember Me" />  </td> </tr> <tr> <td colspan="2" align="center"> <br />  <input id="btnLogin" runat="server"   type="button" value="Login" />  </td> </tr> <tr> <td colspan="2" align="center"> <br /> <input type="button" value="Attempt Access without Login" onclick="document.location='Home.aspx'" /> </td> </tr> </table> </td> </tr> </table> </form> </body> </html> 

Example 8-3. Login page code-behind (.vb)
 Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: Login.aspx.vb ' ' Description: This module provides the code behind for the ' Login.aspx page ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System.Configuration Imports System.Data Imports System.Data.OleDb Imports System.Web.Security Imports System.Web.UI.HtmlControls Imports System.Web.UI.WebControls Namespace ASPNetCookbook.VBSecurity81 Public Class Login Inherits System.Web.UI.Page 'controls on the form Protected txtLoginID As TextBox Protected txtPassword As TextBox Protected chkRememberMe As CheckBox Protected WithEvents btnLogin As HtmlInputButton '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load 'Put user code to initialize the page here End Sub 'Page_Load '************************************************************************* ' ' ROUTINE: btnLogin_ServerClick ' ' DESCRIPTION: This routine provides the event handler for the login ' button click event. It is responsible for authenticating ' the user and redirecting to the next page if the user ' is authenticated. '------------------------------------------------------------------------- Private Sub btnLogin_ServerClick(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnLogin.ServerClick 'name of querystring parameter containing return URL  Const QS_RETURN_URL As String = "ReturnURL"  Dim dbConn As OleDbConnection Dim dCmd As OleDbCommand Dim dr As OleDbDataReader Dim strConnection As String Dim strSQL As String Dim nextPage As String Try  'get the connection string from web.config and open a connection   'to the database   strConnection = _   ConfigurationSettings.AppSettings("dbConnectionString")   dbConn = New OleDb.OleDbConnection(strConnection)   dbConn.Open( )   'check to see if the user exists in the database   strSQL = "SELECT (FirstName + ' ' + LastName) AS UserName " & _   "FROM AppUser " & _   "WHERE LoginID=? AND " & _   "Password=?"   dCmd = New OleDbCommand(strSQL, dbConn)   dCmd.Parameters.Add(New OleDbParameter("LoginID", _   txtLoginID.Text))   dCmd.Parameters.Add(New OleDbParameter("Password", _   txtPassword.Text))   dr = dCmd.ExecuteReader( )   If (dr.Read( )) Then   'user credentials were found in the database so notify the system   'that the user is authenticated   FormsAuthentication.SetAuthCookie(CStr(dr.Item("UserName")), _   chkRememberMe.Checked)   'get the next page for the user   If (Not IsNothing(Request.QueryString(QS_RETURN_URL))) Then   'user attempted to access a page without logging in so redirect   'them to their originally requested page   nextPage = Request.QueryString(QS_RETURN_URL)   Else   'user came straight to the login page so just send them to the   'home page   nextPage = "Home.aspx"   End If   'Redirect user to the next page   'NOTE: This must be a Response.Redirect to write the cookie to the   ' user's browser. Do NOT change to Server.Transfer which   ' does not cause around trip to the client browser and thus   ' will not write the authentication cookie to the client   ' browser.   Response.Redirect(nextPage, True)   Else   'user credentials do not exist in the database - in a production   'application this should output an error message telling the user   'that the login ID or password was incorrect   End If  Finally 'cleanup If (Not IsNothing(dr)) Then dr.Close( ) End If If (Not IsNothing(dbConn)) Then dbConn.Close( ) End If End Try End Sub 'btnLogin_ServerClick End Class 'Login End Namespace 

Example 8-4. Login page code-behind (.cs)
 //---------------------------------------------------------------------------- // // Module Name: Login.aspx.cs // // Description: This module provides the code behind for the // Login.aspx page // //**************************************************************************** using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Web.Security; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace ASPNetCookbook.CSSecurity81 { public class Login : System.Web.UI.Page { // controls on the form protected System.Web.UI.WebControls.TextBox txtLoginID; protected System.Web.UI.WebControls.TextBox txtPassword; protected System.Web.UI.WebControls.CheckBox chkRememberMe; protected System.Web.UI.HtmlControls.HtmlInputButton btnLogin; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { // wire the login button this.btnLogin.ServerClick += new EventHandler(this.btnLogin_ServerClick); } // Page_Load //************************************************************************ // // ROUTINE: btnLogin_ServerClick // // DESCRIPTION: This routine provides the event handler for the login // button click event. It is responsible for // authenticating the user and redirecting to the next // page if the user is authenticated. // //------------------------------------------------------------------------ private void btnLogin_ServerClick(Object sender, System.EventArgs e) { // name of querystring parameter containing return URL  const String QS_RETURN_URL = "ReturnURL";  OleDbConnection dbConn = null; OleDbCommand dCmd = null; OleDbDataReader dr = null; String strConnection = null; String strSQL = null; String nextPage = null; try {  // get the connection string from web.config and open a connection   // to the database   strConnection =   ConfigurationSettings.AppSettings["dbConnectionString"];   dbConn = new OleDbConnection(strConnection);   dbConn.Open( );   // check to see if the user exists in the database   strSQL = "SELECT (FirstName + ' ' + LastName) AS UserName " +   "FROM AppUser " +   "WHERE LoginID=? AND " +   "Password=?";   dCmd = new OleDbCommand(strSQL, dbConn);   dCmd.Parameters.Add(new OleDbParameter("LoginID",   txtLoginID.Text));   dCmd.Parameters.Add(new OleDbParameter("Password",   txtPassword.Text));   dr = dCmd.ExecuteReader( );   if (dr.Read( ))   {   // user credentials were found in the database so notify the system   // that the user is authenticated   FormsAuthentication.SetAuthCookie((String)(dr["UserName"]),   chkRememberMe.Checked);   // get the next page for the user   if (Request.QueryString[QS_RETURN_URL] != null)   {   // user attempted to access a page without logging in so redirect   // them to their originally requested page   nextPage = Request.QueryString[QS_RETURN_URL];   }   else   {   // user came straight to the login page so just send them to the   // home page   nextPage = "Home.aspx";   }   // Redirect user to the next page   // NOTE: This must be a Response.Redirect to write the cookie to   // the user's browser. Do NOT change to Server.Transfer   // which does not cause around trip to the client browser   // and thus will not write the authentication cookie to the   // client browser.   Response.Redirect(nextPage, true);   }   else   {   // user credentials do not exist in the database - in a production   //application this should output an error message telling the user   // that the login ID or password was incorrect.   }  } // try finally { // cleanup if (dr != null) { dr.Close( ); } if (dbConn != null) { dbConn.Close( ); } } // finally } // btnLogin_ServerClick } // Login } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2006
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net