Using login controls for an existing data source by creating a custom membership provider in ASP.NET 2.0 (910440)
The information in this article applies to:
ASP.NET Support Voice columnUsing login controls for an existing data source by creating a custom membership provider in ASP.NET 2.0To customize this column to your needs, we want to invite you to submit your ideas about topics that interest you and issues that you want to see addressed in future Knowledge Base articles and Support Voice columns. You can submit your ideas and feedback using the Ask For It form. There's also a link to the form at the bottom of this column.IntroductionHello and welcome back! My name is Parag Agarwal, and I'm a support engineer
here at Microsoft. This month we are going to discuss another cool feature in Microsoft ASP.NET 2.0, managing
providers for login controls.OverviewIn this month's column, I will discuss the following topics:
- Brief overview of login controls
- Brief overview of the provider model in ASP.NET 2.0
- Walkthrough on creating a custom provider that can be used
by login controls with an existing data source
Login controlsIt's a very common requirement to have login functionality in
almost every Web application. Before ASP.NET 2.0 was released, we used to
design user interfaces (UI) for authenticating the user. This involved writing a lot of redundant code. To avoid this, ASP.NET
2.0 provides a complete login solution in the form of a bunch of server controls for
Web applications that require no programming. Internally, these controls
are responsible for rendering the appropriate UI where a user can enter his or her
credentials and validate them. Now, we don't have to design the UI as a page
developer, and we don't need to take care of authenticating the user by
writing our own code. The underlying provider model used by the Login control
takes care of that. We will see how the Login control uses the providers for
authenticating the user in the next section. You can find more information about login
controls and how we can use them at the following Web site: Provider modelThe provider model allows developers to build pluggable software. It is basically
intended to decouple an abstraction from the implementation so that both pieces
can vary independently. In order to do this, ASP.NET provides certain abstract
base classes that have all the abstract methods and properties required to be
implemented by the deriving class that provides the implementation for those
methods and properties. For more information on abstract base
classes, visit the following Web site: So, to summarize,
providers are used as intermediaries by controls to
interact with a data store. They provide abstraction between the application and the data
source in the same way device drivers provide abstraction from a hardware
device. Because this article talks about membership providers, we will
discuss what classes ASP.NET 2.0 provides for the membership feature. The ASP.NET
2.0 membership feature defines an abstract base class called the MembershipProvider class. Furthermore, MembershipProvider derives from a different base class
called the ProviderBase class, which is a common class to all the providers. Therefore, developers can
create their own provider classes by deriving the existing MembershipProvider
class. For more information on the MembershipProvider and ProviderBase classes, visit the following Web sites: Once we have defined the membership provider, it must be described
in a configuration file, either in Machine.config (for all Web applications) or
in Web.config (for a specific Web application). The appropriate provider is
instantiated at run time from the information provided in the configuration
file by ASP.NET. However, it is possible to change the provider dynamically at
run time as well. For more information on specifying the configuration
settings for a membership provider, visit the following Web site: Right now, the Login control is shipped with two built-in membership
providers that use a specific data scheme/data structure: - Active Directory
The associated provider class is System.Web.Security.ActiveDirectoryMembershipProvider. - Microsoft SQL Server
The associated provider class is System.Web.Security.SqlMembershipProvider.
However, if we want to work with an existing database
structure, we can easily code a custom membership provider to get login controls
to talk to the old database structure. Creating a custom membership providerNow that we have enough information on login controls and the
underlying provider model that they use, let's create a custom membership
provider to get existing login controls to work against a custom data store.
Note The custom provider will use a SQL Server database called TestDB. TestDB will have a table named Users with the fields UserID, UserName, and Password and other information, such as e-mail ID and address.
- Start Microsoft Visual Studio 2005.
- Create a class library project, and give it a name, for
example, CustomMembershipProviderLib.
- Add a source file to the project, for example,
CustomMembershipProvider.cs.
- Include System.Web and System.Configuration in the references section.
- Verify that the following namespaces are included in
the CustomMembershipProvider.cs file.
using System;
using System.Web;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web.Security;
using System.Collections.Specialized;
using System.Data.SqlClient; - Inherit the CustomMembershipProvider class with the MembershipProvider class.
class CustomMembershipProvider :
MembershipProvider - As we already know, MembershipProvider is an abstract class, so we need to override all the abstract
methods in the CustomMembershipProvider class. There is a very cool feature in Visual Studio 2005 that does this automatically. As
soon as you extend any abstract class, just right-click Abstract
class, and then click Implement Abstract
Class. This automatically places declarations for all the abstract
methods. You will notice that the body for each method contains a common line
of code.
throw new Exception("The method or operation is not implemented."); This indicates what features are supported by the custom
provider.
Note Implementation for the Initialize method is mandatory. - In the custom provider, we will concentrate on providing a few
features such as the following:
- Creating a new user by
using the CreateUserWizard
control
- Validating the user credentials by using the Login
control
We will implement these features one by one. First, implement the Initialize method. This method is called by ASP.NET when the provider is loaded.
Also, providers are loaded when the application uses them for the first time, and they are
created once per application domain.public override void Initialize(string name,NameValueCollection config)
{
// Verify that config isn't null
if (config == null)
throw new ArgumentNullException("config");
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(name))
name = "AspNetCustomMembershipProvider";
// Add a default "description" attribute to config if the
// attribute doesn't exist or is empty
if (string.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Custom SQL Provider");
}
// Call the base class's Initialize method
base.Initialize(name, config);
} - Next, implement the ValidateUser method. It takes the input user name and password and verifies that
the membership data source contains a matching user name and password. If the
method returns true, the Login control allows the user to pass through the
verification. Otherwise, it asks for the credentials again.
public override bool ValidateUser(string username, string password)
{
SqlConnection cnn = null;
SqlCommand cmd = null;
bool userExists = true;
try
{
cnn = new SqlConnection();
cnn.ConnectionString = "connection string for the existing data source";
cnn.Open();
string selectQry = "Select query for username and password";
cmd = new SqlCommand(selectQry, cnn);
SqlDataReader rdr = cmd.ExecuteReader();
if (!rdr.Read())
userExists = false;
}
catch (Exception ex)
{
throw ex;
}
finally
{
cmd.Dispose();
cnn.Close();
}
return userExists;
} - Implement one more method called CreateUser that is called by the CreateUserWizard control. It takes input,
such as user name, password, e-mail address, and other information, and adds a new user to
the existing membership data source. It returns
the MembershipUser
object, which represents a newly created user. It also sets MembershipCreateStatus,
which tells whether the user was successfully created. If the user was not successfully created, we can
specify the reason.
public override MembershipUser CreateUser(string username, string
password, string email, string passwordQuestion, string
passwordAnswer, bool isApproved, object providerUserKey,
out MembershipCreateStatus status)
{
SqlConnection cnn = null;
SqlCommand cmd = null;
MembershipUser newUser = null;
try
{
cnn = new SqlConnection();
cnn.ConnectionString = "connection string for the existing data source";
cnn.Open();
string insertQry = "Prepare the Insert query...";
cmd = new SqlCommand(insertQry, cnn);
cmd.ExecuteNonQuery();
// Right now I am giving default values for DateTime
// in Membership constructor.
newUser = new MembershipUser(
"AspNetCustomMembershipProvider",
username, null, String.Empty, String.Empty,
String.Empty, true, false, DateTime.Now,
DateTime.Now, DateTime.Now, DateTime.Now,
DateTime.Now
);
status = MembershipCreateStatus.Success;
}
catch (Exception ex)
{
status = MembershipCreateStatus.ProviderError;
newUser = null;
throw ex;
}
finally
{
cmd.Dispose();
cnn.Close();
}
return newUser;
} - The rest of the methods look like those given below. If you
wish, you can implement any of them.
// MembershipProvider Properties
public override string ApplicationName
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override bool EnablePasswordRetrieval
{
get { return false; }
}
public override bool EnablePasswordReset
{
get { return false; }
}
public override int MaxInvalidPasswordAttempts
{
get { throw new NotSupportedException(); }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { throw new NotSupportedException(); }
}
public override int MinRequiredPasswordLength
{
get { throw new NotSupportedException(); }
}
public override int PasswordAttemptWindow
{
get { throw new NotSupportedException(); }
}
public override MembershipPasswordFormat PasswordFormat
{
get { throw new NotSupportedException(); }
}
public override string PasswordStrengthRegularExpression
{
get { throw new NotSupportedException(); }
}
public override bool RequiresQuestionAndAnswer
{
get { return false; }
}
public override bool RequiresUniqueEmail
{
get { return false; }
}
public override MembershipUser GetUser(string username,
bool userIsOnline)
{
throw new NotSupportedException();
}
public override MembershipUserCollection GetAllUsers(int pageIndex,
int pageSize, out int totalRecords)
{
throw new NotSupportedException();
}
public override int GetNumberOfUsersOnline()
{
throw new NotSupportedException();
}
public override bool ChangePassword(string username,
string oldPassword, string newPassword)
{
throw new NotSupportedException();
}
public override bool
ChangePasswordQuestionAndAnswer(string username,
string password, string newPasswordQuestion,
string newPasswordAnswer)
{
throw new NotSupportedException();
}
public override bool DeleteUser(string username,
bool deleteAllRelatedData)
{
throw new NotSupportedException();
}
public override MembershipUserCollection
FindUsersByEmail(string emailToMatch, int pageIndex,
int pageSize, out int totalRecords)
{
throw new NotSupportedException();
}
public override MembershipUserCollection
FindUsersByName(string usernameToMatch, int pageIndex,
int pageSize, out int totalRecords)
{
throw new NotSupportedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotSupportedException();
}
public override MembershipUser GetUser(object providerUserKey,
bool userIsOnline)
{
throw new NotSupportedException();
}
public override string GetUserNameByEmail(string email)
{
throw new NotSupportedException();
}
public override string ResetPassword(string username,
string answer)
{
throw new NotSupportedException();
}
public override bool UnlockUser(string userName)
{
throw new NotSupportedException();
}
public override void UpdateUser(MembershipUser user)
{
throw new NotSupportedException();
} - Compile the class library project. It will generate the DLL
output.
- Open an existing Web site, or create a new Web site.
- Add the DLL reference in the Web site.
- Register the provider in the Web.config file as follows.
<membership defaultProvider="AspNetCustomMembershipProvider">
<providers>
<clear />
<add name="AspNetCustomMembershipProvider" type="CustomMembershipProvider"/>
</providers>
</membership> - Add a Web Forms page named Login.aspx where the Login control
can be used.
<form id="Form1" runat="server">
<div>
<asp:Login ID="Login1" runat="server"></asp:Login>
</div>
</form> - Add another Web Forms page named CreateUser.aspx
where the CreateUserWizard control can be used.
<form id="Form1" runat="server">
<div>
<asp:CreateUserWizard ID="CreateUserWizard1" runat="server"></asp:CreateUserWizard>
</div>
</form> - Run both of the Web Forms pages, and you will see the
output.
If you are not using Visual Studio, you can perform the
following steps:
- Open any text editor.
- Create a file named CustomMembershipProvider.cs, and follow
the instructions given in steps 5 through 17.
- Create a directory under the wwwroot folder.
- Start Microsoft Internet Information Services (IIS)
Manager, and mark the new directory as the virtual root directory. Also, ensure
that it is configured to run under the Microsoft .NET Framework 2.0 in case another version of the .NET Framework is installed on the computer.
- Copy the Web Forms pages and Web.config in that
directory.
- Create an App_Code folder under the new
directory.
- Copy the CustomMembershipProvider.cs file in the App_Code
folder.
- Run the CreateUser.aspx Web Forms page from IIS Manager.
ConclusionThat's all for now on custom membership providers. I hope that
this column will help you understand the basics of creating custom
membership providers and how they provide abstraction to the end
user. Thank you for your time. We expect to write more about the
providers that are provided by ASP.NET 2.0 and how we can extend them to customize their
behavior according to our needs. For more information about
providers, visit the following Web sites:
Modification Type: | Major | Last Reviewed: | 1/26/2006 |
---|
Keywords: | kbProgramming kbSecurity kbcode kbhowto KB910440 kbAudDeveloper |
---|
|