|I l @ ve RuBoard|
In the sections that follow is a simple authentication application that uses Forms-based Authentication to control login access to a page with this chapter's text. The source code highlights the major points of discussion in this chapter and includes a few extras. If you don't have VS.NET yet, I've also included a few short movies that show the application running in this environment. You can find these files on the Web site.
The Application Root
Before we dig into the code, the first step after you have ASP.NET installed is to open your Internet Service Manager and make sure the application is registered accordingly . Just drag and drop the Chapter10App directory onto your system and create a virtual directory for it. If you look at the figure below, you'll see that I've named my application Chapter10App. The root domain name is planetdev.com so the URL for this application would look like http://www.planetdev.com/Chapter10App . In Figure 10.3, you can see the main files for our application as they appear in the Internet Service Manager window.
Figure 10.3. The Chapter 10 Application as it appears in the Internet Service Manager.
If you right-click the applications root directory, and select Properties, you'll see an interface that allows you to configure some general settings as well as security, HTTP, and Session settings for your application. You can programmatically change much of the same information in your web.config file, but you should at least become familiar with the server administration GUI as well. In Figure 10.4 you'll see the property settings window as well as the settings you can change if you click the Configuration button.
Figure 10.4. You can configure settings for your application through the Internet Service Manager GUI or programmatically in the web.config file.
Now that you've got everything configured the way you want, let's look at a simple high-level diagram that describes the relationships of the objects that drive the application (see Figure 10.5).
Figure 10.5. A beginner-friendly diagram of the file relationships in this chapter's ASP.NET application.
This application is even simpler than the diagram looks. As we discussed earlier, when someone makes a request (HTTP Request) to the URL where your application resides, it starts a chain of events that include declaring session variables , defining configuration and security settings, and executing the code behind the requested aspx page that handles events. The Chapter10Login.aspx page will work with the global.asax and web.config files to configure the data storage and configuration settings for a new client session. It will also import and inherit functionalities that will authenticate a user's login. When a user submits their username and password, it will use the data stored in the web.config file to authenticate the client. If the client is authorized, he will be directed to the Chapter10Text.aspx page. If he is denied access, the label control on the Chapter10.aspx page will ask him to resubmit his data.
Before we dive into some new code let's get a more detailed look at the global and configuration setting files you've already seen in this chapter.
Note: You will see many of the same comments in the source code of the application itself.
Both of these files are critical to defining your ASP.NET application's execution (runtime) environment. The first job of the global.asax file is to import the critical .NET class libraries that are used throughout the application. The web.config file will be used to manage the security roles and privileges for clients requesting its services.
The first two lines of the global.asax file import two of the System namespace's class libraries that are required for ASP.NET applications.
Imports System.Web Imports System.Web.SessionState
As mentioned earlier in the book, an important part of learning how to build .NET applications is learning what the included .NET class libraries can do for you. .NET's class library is organized a bit like the directory of folders and files on your hard drive. Your operating system prefers that you use back- slashes "\" to navigate to a specific file or folder (such as c:\Windows\WebFiles\. Object Oriented Programming compilers prefer that we use a dot "." to navigate the class hierarchy (for example, System. Web.Session
Public Class Global Inherits System.Web.HttpApplication
After we have imported our namespace classes, we begin the code that describes this file as a public class, which will need to inherit functionality found in the HttpApplication class.
Listing 10.1 Events and variable declarations in the global.asax file. (global.asax)
Sub Application_OnStart() Application("UsersOnline") = 0 End Sub Sub Session_OnStart() Session("YourName") = "" Session("YourWallet") = "" Application("UsersOnline") = Application("UsersOnline") + 1 End Sub Sub Session_OnEnd() Session objects are destroyed and the Application variable is updated. Application("UsersOnline") = Application("UsersOnline") - 1 End Sub Sub Application_OnEnd() Application is shutting down all objects are destroyed End Sub
We've already discussed this section of code earlier in the chapter, so I'll just give you a history of what has happened since the client called your application ”ignore the end events for now. Assuming it's the very first client HTTP request, the ASP.NET will load and fire the Application_ OnStart event and create a variable called UsersOnline . It has also fired the Session_OnStart event and created two variables.
Public Sub New() MyBase.New() End Sub
I should also note that the New method replaces the Class_Initialize event some of you VB programmers might be familiar with.
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs) ' Fires at the beginning of each HTTP request End Sub Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs) ' Fires when attempting to authenticate the user End Sub Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) ' Fires when an error occurs End Sub
These are the new events ASP.NET added to what used to be the global.asa in Classic ASP. The extended functionality is good to know about but I don't need it for this simple application.
ASP.NET gives source code authors the power and responsibility to configure their own applications and system-level settings. In the past these settings were contained in IIS meta tables and set by the system administrator. Now you can use the web.config file to handle everything for you. In our Chapter10App, I've chosen to use Forms-based authentication to demonstrate how we can use web.config to configure and authorize users' names and passwords and their credentials.
NOTE: The objects you will want to make reference to and configure are case sensitive. Keep the appendix for this book handy!
<configuration> <system.web> <sessionState timeout="5" />
The <configuration> tag is the wrapper for specific settings you will use in your application. The first line includes a reference to the Web classes in the System namespace. Once I have this reference, I can access a multitude of properties that include booting clients after five minutes of inactivity.
<authentication mode="Forms"> <forms name="Chapter10" loginUrl="Chapter10Login.aspx" protection="All" timeout="20" path="/">
The code you see above sets our application's authentication mode to Forms . In order for you to use this authentication model, make sure you have set the proper page URL that manages the form-based login process.
<credentials passwordFormat="Clear"> <user name="FrankZ" password="yellowshark"> <user name="BobbyB" password="StrictlyCommercial" /> <user name="GlennC" password="human" /> </credentials> </forms> </authentication>
Here is where we define our users and any credential settings. In this case I have chosen to keep things simple by setting the passwordFormat to Clear. You can change it to one of the encryption schemes we mentioned earlier if you'd like.
<authorization> <allow users="FrankZ,BobbyB,GlennC" /> <deny users="*"/> </authorization> </system.web> </configuration>
Before we close any open XML tags, we indicate the users we will grant access and everyone else will be denied.
Figure 10.6 shows the HTML page as it would appear to a client who visits the application. The code behind page that manages the user events at the server is called Chapter10Login.aspx.vb.
Figure 10.6. The main page of our application (Chapter10Login.aspx) as it appears in Internet Explorer.
This is the page and code that really does all the work. I must note, however, that while writing this entire application, I never wrote a bit of HTML (and I loved every minute of it). The presentation code (Chapter10Login.aspx) is completely separate from the code behind page. I'm only going to mention the aspx page because I haven't even looked at its code since I made sure it inherited my code behind page. The only line of code you or I should care about in that aspx page is the first line:
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Chapter10Login.aspx.vb" Inherits="Chapter10ByExample.Chapter10LoginCode"%>
The code above is essentially inheriting the code behind page's class object called Chapter10LoginCode. In my project file I had assigned the application namespace as "Chapter10ByExample" so I drill down the hierarchy separating objects with the dot. You'll see that by inheriting that one class, I've also inherited 90 percent of this application's logic.
In this case we'll import the security class that we use for authentication in the code below. The second line establishes that this is a public class object named Chapter10LoginCode .
Imports System.Web.Security Public Class Chapter10LoginCode
The line you see below inherits the System's Page object, which our aspx page needs to inherit from in order to expose the event handlers in this file.
The following lines were generated automatically by VS.NET as I dragged controls from my toolbox and dropped them onto my Web page. All they are saying is that the code behind page should listen for events coming from objects which have inherited it.
Protected WithEvents txtName As System.Web.UI.WebControls.TextBox Protected WithEvents txtPass As System.Web.UI.WebControls.TextBox Protected WithEvents lblMessage As System.Web.UI.WebControls.Label Protected WithEvents txtCookie As System.Web.UI.WebControls.TextBox Protected WithEvents btnYes As System.Web.UI.WebControls.RadioButton Protected WithEvents btnNo As System.Web.UI.WebControls.RadioButton Protected WithEvents lblCookie As System.Web.UI.WebControls.Label Protected WithEvents cmdLogin As System.Web.UI.WebControls.Button
The following section of code represents the event handlers that are fired as a user interacts with our aspx page. One incredibly exciting feature I must mention, which I used in this application and was mentioned earlier in the book, is a control's autopostback feature. It allows you to update a control without reloading the entire page. For example, in this application, if a user is denied access I simply update a label control to indicate they are not an authorized user. All you have to do is change a control's autopostback property to "True" and handle the events accordingly. The code below shows the autopostback feature and also checks to see if they have added a cookie to their system previously.
Listing 10.2 The 'Code Behind' Reading and Writing a Cookie to a User's System (Chapter10Login.aspx.vb)
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load lblSessionID.Text = "Your Session ID is " & Session.SessionID ' Show them their Session ID Dim objCookie As HttpCookie 'By importing the System.Web namespace you can create an instance of the HTTPCookie Class objCookie = Request.Cookies("Chapter10") If Not IsNothing(objCookie) Then 'If the value is not null then we can great them accordingly lblMessage.ForeColor = lblMessage.ForeColor.Black() lblMessage.Text = "Welcome back, I recognized you by your childish cookie! Please login to view Chapter10 online." ElseIf Session("YourName") = "" Then ' We're checking a Session Object variable for any string data. lblMessage.ForeColor = lblMessage.ForeColor.Black() lblMessage.Text = "Welcome, please login to view Chapter10 online." Else ' Ok, something's strange here! They have a string data in their ' Session("YourName") variable. They must have been sent back to us. lblMessage.ForeColor = lblMessage.ForeColor.Black() lblMessage.Text = "Unauthorized Access: I'm sorry " & Session("YourName") & ", please check your username and password " & " and try again." End If End Sub
VS.NET automatically creates a Page_Load event for your Web forms as you add them to your project. First, we'll use this event to check to see if they have our cookie on their system. When you save a cookie on a user's system, they will be passing the cookie data in their HTTP headers. We can request to see them (if they exist), and we can also edit them if we like. In order to check for a cookie, we have to have a reference to the HTTPCookie class, which is part of the System.Web namespace. If the cookie is not Null then we know they have been here before. Then we check the Session Object data to see if the Session("YourName") variable contains any string data. If it's empty, it's pretty safe to assume they haven't tried to log in yet. If that variable has string data, it indicates that they either left the section of the app that requires access and need to login, or that they have been rejected by our security class.
The following code simply halts the login process if the length of string data submitted looks suspicious.
Listing 10.3 Using server-side code to manage client events. (Chapter10Login.aspx.vb)
Private Sub cmdLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdLogin.Click If Len(txtName.Text) <= 5 Then lblMessage.ForeColor = lblMessage.ForeColor.Red() lblMessage.Text = "Please enter a username." Exit Sub End If If Len(txtPass.Text) <= 3 Then lblMessage.ForeColor = lblMessage.ForeColor.Red() lblMessage.Text = "Please enter a password." Exit Sub End If
If everything looks good we'll write a little cookie to their system before proceeding to the section of code that handles the authentication process.
If btnYes.Checked = True Then Response.Cookies("Chapter10")("Childish") = txtCookie.Text Response.Cookies("Chapter10")("Mature") = txtCookie2.Text Response.Cookies("Chapter10").Expires = "3/3/2039" End If
When you send the HTTP response, you simply load it with your cookie(s). If cookies are enabled on the client browser, it's extremely simple to save (persist) this data on their machine. This data is now stored on their system and will exist in their headers across all their sessions until the cookie expires or they have deleted it. While it would have been very easy to authenticate users with cookies, I avoided it because of the level of abuse I have seen recently by deceitful Web sites who are pooling their resources together to share cookie data. If you would like more info on this issue, a search for "WebBugs" will lead you in the right direction. In the meantime, let's look at the authentication method we did choose.
If FormsAuthentication.Authenticate(txtName.Text, txtPass.Text) = True Then Session("YourName") = txtName.Text Response.Redirect("Chapter10Text.aspx?Name=" & txtName.Text) Else lblMessage.Text = "Unauthorized Access: I'm sorry " & Session("YourName") & ", please check your username and password and try again." End If End Sub
Since we have imported our security classes and configured them in web.config, we can now use a method to test them out. The FormsAuthentication.Authenticate method requires two arguments that we have extracted from the text property of the txtName and txtPass controls. If the system recognizes them from our web.config file, it returns a Boolean "True" to indicate they are authorized. Figure 10.7 shows what an authorized user will see. If they are authorized to enter, we redirect them to the Chapter 10 text and send them on their way with a query string that includes their name. We'll extract this from their HTTP headers in the Page_Load event of the Chapter10Text.aspx.vb page.
Figure 10.7. The application will check the usernames and passwords stored in the web.config file to authenticate a user's access to Chapter10Text.aspx.
Listing 10.4 Handling client-side events with server-side code to update controls in the client's browser. (Chapter10Login.aspx.vb)
Private Sub btnYes_CheckedChanged(ByVal sender As System.Object, ByVal e As System. EventArgs) Handles btnYes.CheckedChanged ' NOTE: By assigning both of these radio buttons a shared group name .NET treat ' them like a control array cmdLogin.Enabled = False lblCookie.Visible = True txtCookie.Visible = True lblCookie2.Visible = True txtCookie2.Visible = True End Sub Private Sub btnNo_CheckedChanged(ByVal sender As System.Object, ByVal e As System. EventArgs) Handles btnNo.CheckedChanged cmdLogin.Enabled = True lblCookie.Visible = False txtCookie.Visible = False lblCookie2.Visible = False txtCookie2.Visible = False End Sub Private Sub txtCookie_TextChanged(ByVal sender As System.Object, ByVal e As System. EventArgs) Handles txtCookie.TextChanged cmdLogin.Enabled = True End Sub End Class
|I l @ ve RuBoard|