Authentication refers to the process of identifying who a user is and whether he has rights to enter your system. How you authenticate a user is completely up to you: You can ask a user for his e-mail address and a password that you have assigned to him. You can ask him for a user ID and password, or you might ask him for his first name, last name, and Social Security number. It all depends on what type of site you are asking the user to log in to and what information is most relevant to you and to the user.
Authenticating users can be done in one of several ways, including the following:
Windows authentication. Windows authentication allows you to use Basic or Digest security, or any of the built-in techniques Windows provides to authenticate users, such as NTLM or Kerberos. You'll most likely use Windows authentication in an intranet scenario, because every user you authenticate must be able to supply credentials within a Windows domain. It's unlikely that you'd use this technique for a public Web site.
Forms-based authentication. When you use forms-based authentication, you can have ASP.NET automatically redirect a user to a specific page on which he must supply login credentials. Once you've validated the user (perhaps by looking up his information in a database), it's up to your code to indicate to the ASP.NET page framework that the current user is valid. Doing so assigns a cookie to the user. The ASP.NET page framework verifies whether the cookie exists, and if it does, displays the page the user originally attempted to view. If the cookie doesn't exist, ASP.NET redirects the user back to the login page. When you use forms-based authentication, it's up to your application to verify the validity of users. This behavior is different from what you'll see with Windows- or Passport-based authorization. In both those other scenarios, authentication is out of your hands it's handled by Windows or by Microsoft's secure servers. You would most likely use forms-based authentication on an Internet site.
Microsoft Passport. Microsoft Passport is a centralized authority that handles authentication for you. Passport requests credentials from users, and if they're part of the Passport system and are successfully validated, the Passport service sends your application a certificate so you can verify the users. Using Passport authentication requires a live Internet connection, of course, and it's unlikely you'll use this technique for intranet applications, where it would be simpler to use Windows authentication to validate your users.
Rather than attempting to cover all the issues involved with ASP.NET security (a topic that would require far more space and depth than this book can provide), this chapter focuses on adding a simple forms-based authentication scheme to your application. You'll allow certain users and disallow others, based on data in the Northwind sample database and in the Web.config file.
Denying Anonymous Users
Your first step in securing your site is to deny access to anonymous users. To do that, you'll need to modify the Web.config file as well as add a little code to the Login.aspx page you created earlier in this book.
To add support for denying anonymous users, follow these steps:
Open your project in Visual Studio .NET. In the Solution Explorer window, double-click the Web.config file to load it into the code editor window.
Find and modify the <authentication> element so that it looks like this:
<authentication mode="Forms"> <forms name="NorthwindApp" loginUrl="Login.aspx" /> </authentication>
XML is case sensitive! Make sure you type in the code exactly as you see it here.
If you set the <authentication> element's mode attribute to Forms (as you've done here), you must supply the forms element containing information about how you want to authenticate users. This element can contain several different attributes. In this example, the following attributes are used:
name. Specifies the cookie to be sent in the HTTP header that will be used for authentication. By default, the cookie name is .ASPXAUTH. In this example, you'll use NorthwindApp as the cookie name.
loginUrl. Specifies the page to redirect to if no valid login cookie is found. By default, ASP.NET will redirect to a page named default.aspx.
You also need to explicitly deny access to anonymous users to enable the redirection to the login page. To do that, modify the <authorization> element in Web.config so that it looks like this:
<authorization> <deny users="?" /> </authorization>
Inside the <authorization> element, you can allow or deny users by using the <allow> or <deny> element, respectively. In this case, you've selected to deny anonymous users. You have several options for building lists of users. Specifically, you can use the following to allow or deny access, depending on the element you include:
?. Represents anonymous users.
*. Represents all users.
A specific name. Represents a specific user ("George", for example).
DomainName\UserName. Represents a Windows domain user.
DomainName\RoleName. Represents a member of a particular Windows role.
When users attempt to browse to your site, they'll immediately be redirected to Login.aspx in order to log in. You must modify that page so that it informs the ASP.NET page framework that you've successfully validated the user.
For now, Login.aspx won't attempt to perform any verification it'll simply inform ASP.NET that any user who visits the page is indeed a valid user.
To add support for verification to your page, follow these steps:
In the Solution Explorer window, double-click Login.aspx to load it into the page designer.
On Login.aspx, double-click the Login command button, loading the code editor with the btnLogin_Click procedure displayed.
Scroll to the top of the file and add the following Imports statement:
Modify the btnLogin_Click procedure so that it looks like this (removing the call to Server.Transfer):
Private Sub btnLogin_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnLogin.Click Session("LoginID") = txtLogin.Text Session("Password") = txtPassword.Text FormsAuthentication. _ RedirectFromLoginPage(txtLogin.Text, False) End Sub
The FormsAuthentication class provides an object with shared methods that makes it easier for you to manage authentication tickets. In this case, you're using RedirectFromLoginPage, a method that redirects a user back to the originally requested page (which was most likely, in this case, Main.aspx). When you call RedirectFromLoginPage here, you're passing two parameters:
A string containing the name of the user (for cookie-authentication purposes).
A Boolean value indicating whether ASP.NET should issue a durable cookie (a cookie that's saved across browser sessions). In this case, the code requests a memory cookie.
| || |
At this point, if you've followed the steps carefully, you should be able to run the application, see the Login.aspx page, and enter any value for the login ID. Once you click Login, you should be redirected to the main page.
It's likely that you'll want to authenticate users within your environment based on some table containing information about valid users and their passwords. In this section, you'll add support for validating employees, using the phone extension as the password for each employee.
Adding an ExecuteScalar Method to DataHandler.vb
Given an employee name, you'll want to be able to retrieve the correct password for that employee from the Northwind Employees table. This requirement adds a need for a standard method you can call that will accept a SQL string and return a single value from a table. ADO.NET provides this capability in the ExecuteScalar method of its Command objects (OleDbCommand and SqlCommand). This method returns the value of the first column within the first row of its resulting set of rows, and that's exactly what you need in this case.
To add this functionality to your project, modify the DataHandler.vb class, adding the procedure shown in Listing 24.1.
Listing 24.1 Retrieve a Single Value from an OleDbCommand Object
Public Shared Function ExecuteScalar( _ ByVal SQL As String, _ ByVal ConnectionString As String) As Object Dim dr As OleDbDataReader Dim cmd As New OleDbCommand() Dim Value As Object Try With cmd .Connection = _ New OleDbConnection(ConnectionString) .Connection.Open() .CommandText = SQL Value = .ExecuteScalar() .Connection.Close() End With Catch Throw End Try Return Value End Function
Adding the LoginValid Method
Given a login ID and a password, you'll need to be able to validate a user. In this section, you'll add the LoginValid procedure, shown in Listing 24.2, to do the work for you.
In the Solution Explorer window, right-click Login.aspx and select View Code from the context menu.
Add the procedure shown in Listing 24.2, directly below the btnLogin_Click procedure.
Listing 24.2 Use ExecuteScalar to Determine Whether a Login Is Valid
Private Function LoginValid( _ ByVal LoginID As String, _ ByVal Password As String) As Boolean Dim strSQL As String Dim strConn As String Dim boolFound As Boolean ' Retrieve connection string. strConn = Session("ConnectString").ToString ' Build SQL to count number of valid employees ' found with credentials. strSQL = "SELECT Count(*) FROM Employees " & _ "WHERE LastName = " & _ DataHandler.QuoteString(LoginID) & _ " AND Extension = " & _ DataHandler.QuoteString(Password) Try 'Execute query and return count of valid records boolFound = (CInt(DataHandler. _ ExecuteScalar(strSQL, strConn)) > 0) If Not boolFound Then lblError.Text = _ "Invalid Login ID/Password combination." End If Catch exp As Exception lblError.Text = exp.Message End Try Return boolFound End Function
The LoginValid procedure does its work taking these actions:
It retrieves the standard connection string:
' Retrieve connection string. strConn = Session("ConnectString").ToString
It builds the SQL string requesting the count of rows that match the ID and password supplied by the user:
' Build SQL to count number of valid employees ' found with credentials. strSQL = "SELECT Count(*) FROM Employees " & _ "WHERE LastName = " & _ DataHandler.QuoteString(LoginID) & _ " AND Extension = " & _ DataHandler.QuoteString(Password)
It executes the SQL, returns the count, and compares that count to 0. If the count is greater than 0, you've successfully found a match against the login ID/password pair:
boolFound = (CInt(DataHandler. _ ExecuteScalar(strSQL, strConn)) > 0)
If a match wasn't found, error text is displayed:
If Not boolFound Then lblError.Text = _ "Invalid Login ID/Password combination." End If
In any case, the procedure returns the value of boolFound:
Validating the User
Finally, you must modify the btnLogIn_Click procedure in the Login.aspx page so that it validates the user. Modify the procedure so that it looks like Listing 24.3.
Listing 24.3 Use LoginValid to Validate a User
Private Sub btnLogin_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnLogin.Click Dim strID As String Dim strPwd As String strID = txtLogin.Text strPwd = txtPassword.Text If LoginValid(strID, strPwd) Then If Session("LoginID").ToString = String.Empty Then Session("LoginID") = strID Session("Password") = strPwd FormsAuthentication.RedirectFromLoginPage( _ strID, False) End If Else lblError.Text = "Invalid Login ID/Password." End If End Sub
This code attempts to authenticate the user/password combination. If it succeeds, it checks the Session variable, LogID. If that variable is an empty string, the code fills in the Session variables LogID and Password with the newly entered values. The code then redirects to the main page, passing the user ID.
| || |
Test out the sample application again. If you've entered all the code correctly, you should now only be able to authenticate employees and their passwords (using each employee's Extension field as the password). Table 24.1 lists the valid employee name/password combinations.
Table 24.1. Use One of These Employee/Password Pairs to Log in to the Sample Application
|Last Name ||Extension |
|Buchanan ||3453 |
|Callahan ||2344 |
|Davolio ||5467 |
|Dodsworth ||452 |
|Fuller ||3457 |
|King ||465 |
|Leverling ||3355 |
|Peacock ||5176 |
|Suyama ||428 |
The main page of the sample application includes a Log Out link that allows users to log out from your application, redirecting them back to the main page (thereby allowing them to log in again) once they've logged out. Follow these steps to add support for logging out:
Display NWLeftNav.ascx in the page designer.
Double-click the Log Out LinkButton control, displaying the empty lnkLogOut_Click procedure.
Scroll to the top of the file and add the following Imports statement:
Modify the lnkLogOut_Click procedure so that it looks like this:
Private Sub lnkLogout_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles lnkLogOut.Click FormsAuthentication.SignOut() Session.Abandon() Response.Redirect("Main.aspx") End Sub
Save your project.
The lnkLogout_Click procedure executes three actions:
It calls the shared SignOut method of the FormsAuthentication class. This releases the client authentication ticket on the server.
It calls the Abandon method of the Session object. This cancels the current session, releasing any Session variables in use.
It uses the Redirect method of the Response object to redirect back to the Main.aspx page (of course, the current user has now logged out, and the Session variable has been reset). Because the certificate for the client has been deleted, ASP.NET will redirect the user to the page defined in the loginUrl attribute within the authentication element in Web.config.
| || |
Once again, try out the sample application. This time, click the Log Out link on the main page and verify that you end up back on the login page, logged out and ready to log in again.
Why did we use Response.Redirect to take you back to Main.aspx? Imagine that once you log out, another user might like to step up to the terminal and interact with this application. Once the original user logs out, you've lost the authentication cookie for that user, and any attempts to browse to any page within the application would redirect the user back to the login page (because of the settings in Web.config). To avoid this, the sample application redirects the user to Main.aspx, which has two side effects: It displays Login.aspx immediately (because the authentication cookie has been lost) and also maintains the page to redirect to once another user logs in.