Recipe 9.2. Restricting Access to All Application Pages


Problem

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

Solution

Change the web.config settings of your application to specify Forms authentication, and 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:

     <configuration> <system.web> … <authentication mode="Forms"> <forms name=".ASPNETCookbookVBSecurity91" loginUrl="Login.aspx" protection="All" timeout="30" path="/"> </forms> </authentication> </system.web> </configuration> 

  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:

     <configuration> <system.web> … <authorization> <deny users="?" /> <!-- Deny anonymous user --> <allow users="*" /> <!-- Allow all authenticated users --> </authorization> </system.web> </configuration> 

In the .aspx file for the login page:

  1. Add a Login control.

  2. Customize the Login control as required by your application.

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

  1. Use the Authenticate event handler of the Login control 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 Examples 9-1, 9-2, 9-3 through 9-4. Example 9-1 shows the modifications we make to web.config to restrict access to all pages. Example 9-2 shows the .aspx file for the login page. Examples 9-3 (VB) and 9-4 (C#) show the code-behind class for the login page. Figure 9-1 shows the login page produced by the application.

Figure 9-1. Example login page


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 to allow ASP.NET to handle all authentication. Therefore, the website (or virtual directory) must be set up to allow anonymous access. (We won't take you through setting up anonymous access in IIS since it is easy to do and is 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 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" may 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 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 as 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 /.


requireSSL

Defines if an SSL connection is required to transmit the authentication cookie. The default value is false.


slidingExpiration

Defines if sliding expiration of the authentication cookie is enabled. If set to true, the authentication's cookie time to expire is refreshed on every page request. If set to false, the authentication cookie will expire at the time set when the cookie was created. The default value in ASP.NET 1.0 is true. In ASP.NET 1.1 and 2.0, the default value is false.


defaultUrl

Defines the default URL used for redirection after authentication. The default value is default.aspx. (This attribute is new for ASP.NET 2.0.)


Cookieless

Defines if cookies are used to store the authentication ticket and defines the behavior of the cookies. Possible values are UseCookies, UseUri, AutoDetect, and UseDeviceProfile. UseCookies specifies that cookies will always be used to store the authentication ticket. UseUri specifies that the authentication ticket will always be placed in the URL. For example:

http://localhost/aspnetcookbook2vb/VBSecurity91/(F(ixspTvd6lDSWeKLUrgFVdGet3iGNV6A9WARvWX2wO29SnCl7aSwcMlEcof6-rEuOlBOm4EA_HJQ128T8GJWBFZB7Hf-JwdE5oAc-Bo4d4Es1))/Home.aspx

AutoDetect specifies cookies will be used if the requesting device supports cookies and cookies are enabled. If cookies are not supported or not enabled, the URL will be used for the authentication ticket. UseDeviceProfile specifies the storage location for the authentication ticket will be determined by the capabilities of the requesting device. This is similar to AutoDetect except no attempt is made to determine if cookies are enabled. The determination is made by the device support. The default value is UseDeviceProfile for backward compatibility with ASP.NET 1.x. (This attribute is new for ASP.NET 2.0.)

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

 <authentication mode="Forms"> <forms name=".ASPNETCookbookVBSecurity91" 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 use the asp:Login control or 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 9-1 and in the .aspx file (Example 9-2).

In the code-behind for the login page, use the Authenticate event handler for the asp:Login control (or the login button click event if you created your own) to verify the user credentials. In Examples 9-3 and 9-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 will need 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 username and a Boolean value indicating if 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 unnecessary 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 inflexible. For an example of a more flexible approach that allows you to store additional data in the authentication cookie, see Recipe 9.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 will automatically append 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 redirect them to a fixed page as illustrated in Examples 9-3 (VB) and 9-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. If you use other mechanisms, such as Server.Transfer, the authentication cookie will not be written to the browser; this will result 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 to the original page. By default, this will redirect the user to default.aspx. If your site uses a different home page, set the defaultUrl attribute of the <forms> element to the required filename.


No other code is required. In other words, by using the code (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 will assume the user is not logged in and will redirect the user to the page defined by the loginUrl attribute. If the cookie does exist, ASP.NET will assume the user is authenticated and pass the request on to the requesting page. In addition, when the cookie exists, ASP.NET will create 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 username you added to the authentication cookie, use the line of code shown here:

 

userName = Context.User.Identity.Name

userName = Context.User.Identity.Name;

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

 

FormsAuthentication.SignOut()

FormsAuthentication.SignOut();

ASP.NET can provide security only for files 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. Any other file typessuch as .gif, .jpg, .txt, and .jsare not protected by ASP.NET security. If access to these file types must 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, because of the extra processing required for the images, text files, JavaScript, etc.


See Also

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

Example 9-1. Changes to web.config to restrict access to all pages

 <?xml version="1.0"?> <configuration> <system.web> … <authentication mode="Forms"> >forms name=".ASPNETCookbookVBSecurity91" 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 9-2. Login page (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="Login.aspx.vb" Inherits="ASPNetCookbook.VBExamples.Login" Title="Login" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" > Block Access To All Pages (VB) </div> <div align="center"> <asp:Login  runat="server" TitleText="" UserNameLabelText="Login ID: " PasswordLabelText="Password: " DisplayRememberMe="true" RememberMeText="Remember Me" LabelStyle-Css CheckBoxStyle-Css TextBoxStyle-Css OnAuthenticate="Login1_Authenticate" > <CheckBoxStyle Css /> <TextBoxStyle Css /> <LabelStyle Css /> </asp:Login> <br /> <input type="button" value="Attempt Access without Login" onclick="document.location='Home.aspx'" /> </div> </asp:Content> 

Example 9-3. Login page code-behind (.vb)

 Option Explicit On Option Strict On Imports System Imports System.Configuration Imports System.Data Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code behind for ''' Login.aspx ''' </summary> Partial Class Login Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' 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. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub Login1_Authenticate(ByVal sender As Object, _   ByVal e As AuthenticateEventArgs) 'name of querystring parameter containing return URL Const QS_RETURN_URL As String = "ReturnURL" Dim dbConn As OleDbConnection = Nothing Dim dCmd As OleDbCommand = Nothing Dim dr As OleDbDataReader = Nothing 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 = ConfigurationManager. _ ConnectionStrings("dbConnectionString").ConnectionString 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", _ Login1.UserName)) dCmd.Parameters.Add(New OleDbParameter("Password", _ Login1.Password)) 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")), _ Login1.RememberMeSet) '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 so output error 'message indicating the problem Login1.FailureText = "Login ID or password is incorrect. " & _ "Please check your credentials and try again." 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 'Login1_Authenticate End Class 'Login End Namespace 

Example 9-4. Login page code-behind (.cs)

 using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Web.Security; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code behind for /// Login.aspx /// </summary> public partial class Login : System.Web.UI.Page { ///*********************************************************************** /// <summary> /// 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. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Login1_Authenticate(Object sender,    AuthenticateEventArgs 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 = ConfigurationManager. ConnectionStrings["dbConnectionString"].ConnectionString; 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", Login1.UserName)); dCmd.Parameters.Add(new OleDbParameter("Password", Login1.Password)); 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"]),    Login1.RememberMeSet); // 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 so output error // message indicating the problem Login1.FailureText = "Login ID or password is incorrect. " + "Please check your credentials and try again."; } } // try finally { // cleanup if (dr != null) { dr.Close( ); } if (dbConn != null) { dbConn.Close( ); } } // finally } // Login1_Authenticate } // Login } 



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

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