In the Part 1 and Part 2 we learnt the concept and internal architecture of ASP.NET provider model. We know that the provider model is extensible and one can implement custom providers to suit his requirement. In this part we will develop two custom providers - one for membership and the other for roles.
Well. There can be many reasons. Here are few:
One more reason that I have not listed in the above list is - You may want to use your own table schema instead of using inbuilt one. At first glance it may look odd but it can be a great way to save your work while migrating applications.
Let's decide the requirements for building our custom membership and role providers.
We will be using BinaryIntellect DatabaseHelper open source component for all our database access.
To begin, create a new web site and add two classes called MyMembershipProvider and MyRoleProvider to App_Code folder. For the sake of simplicity we will be creating all the necessary classes in the web site itself. In a more real world situations you may create a separate class library project to contain these classes.
Open the web.config file and add the following markup:
<membership defaultProvider="mymembershipprovider"> <providers> <add name="mymembershipprovider" type="MyMembershipProvider" connectionStringName="connstr"/> </providers> </membership>
<roleManager enabled="true" defaultProvider="myrolesprovider"> <providers> <add name="myrolesprovider" type="MyRolesProvider" connectionStringName="connstr"/> </providers> </roleManager>
Here, we instruct ASP.NET to use MyMembershipProvider class as membership provider and MyRolesProvider class as roles provider.
Recollect from Part 2 that custom membership providers need to inherit from System.Web.Security.MembershipProvider class. The MembershipProvider class in turn inherits from ProviderBase class. The MembershipProvider class contains several abstract methods that you must implement in your class.
If you are using VS.NET then your job is simple. Right click on the MembershipProvider class in the class definition line and choose "Implement Abstract Class". VS.NET will add dummy delimitations for all the required methods and properties from the MembershipProvider class. The following table lists all the properties and methods that you need to implement (methods are shown with parenthesis).
Property/Method Name | Description |
Initialize()* | Receives the connection string name specified in the web.config file. You can use it to perform database operation in your class. |
Name* | Represents name of our custom provider |
CreateUser()* | Creates a user |
UpdateUser()* | Saves modified information about an existing user |
DeleteUser()* | Deletes a user |
GetUser()* | Gets a user as MembershipUser instance |
GetAllUsers()* | Gets all the users as MembershipUserCollection |
ChangePassword()* | Changes password of a user |
GetPassword()* | Retrieves password of a user. Used when implementing "Forgot Password" feature |
ValidateUser()* | Authenticates the user |
EnablePasswordReset* | Indicates whether the password can be reset by the user |
EnablePasswordRetrieval* | Indicates whether the password can be retrieved by teh user |
RequiresQuestionAndAnswer* | Indicates whether user should supply a security question and answer during registration |
RequiresUniqueEmail* | Indicates whether the email supplied during registration should be unique |
ApplicationName | Name of the web application. This name is used in case you are using a central database for storing membership data of multiple applications |
MaxInvalidPasswordAttempts | Indicates the number of times user can try to login to the system |
MinRequiredNonAlphanumericCharacters | Indicates minimum no. of non alpha numeric characters that the user must supply during registration and password change |
MinRequiredPasswordLength | Indicates the minimum length required for the password when user registers or changes the password |
ChangePasswordQuestionAndAnswer() | Allows to change user's security question and answer |
FindUsersByEmail() | Searches user database on the basis of email |
FindUsersByName() | Searches user database on the basis of user name |
GetNumberOfUsersOnline() | Returns total no. of uses that are signed in |
GetUser() | Returns MembershipUser instance representing a specific user |
GetUserNameByEmail() | Returns the user name on the basis of email |
PasswordAttemptWindow | Indicates the time span for multiple login attempts |
PasswordFormat | Indicates the format of password e.g.clear, hashed etc. |
PasswordStrengthRegularExpression | Indicates a regular expression to be used to check the strength of password |
ResetPassword() | Resets the password |
UnlockUser() | Unlocks the user account |
In our example we will code the methods and properties marked with * above. The remaining members will simply throw a "Not implemented" exception.
The complete source code of our custom membership provider can be found in the download (MyMembershipProvider.cs). As an example CreateUser() method implementation is given below:
public override MembershipUser CreateUser (string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { MembershipUser user = new MembershipUser(Name, username, providerUserKey, email, passwordQuestion, null, isApproved, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now); string sql = "INSERT INTO USERS(USERNAME,PASSWORD, EMAIL,ISACTIVE) VALUES(@UID,@PWD,@EMAIL,@ISACTIVE)"; db.AddParameter("@UID", username); db.AddParameter("@PWD", password); db.AddParameter("@EMAIL", email); db.AddParameter("@ISACTIVE", (isApproved == true ? "Y" : "N")); int i = db.ExecuteNonQuery(sql);
if (i > 0) { status = MembershipCreateStatus.Success; return user; } else { status = MembershipCreateStatus.ProviderError; return null; } }
Creating custom roles provider involves creating a class that inherits from RoleProvider class. The following table lists all the properties and methods that you need to implement (methods are shown with parenthesis).
Property/Method Name | Description |
Initialize()* | Receives the connection string name specified in the web.config file. You can use it to perform database operation in your class. |
Name* | Represents name of our custom provider |
CreateRole* | Create a new role |
DeleteRole* | Deletes an existing role |
GetAllRoles* | Returns all roles as string array |
RoleExists* | Checks if role exists in the database |
AddUsersToRoles* | Adds users to specified roles |
RemoveUsersFromRoles* | Removes users from specified roles |
GetRolesForUser* | Returns all the roles for a specific user |
GetUsersInRole* | Returns all the users belonging to a specified role |
IsUserInRole* | Checks if a user exists in a specified role |
ApplicationName | Name of the web application. This name is used in case you are using a central database for storing membership data of multiple applications |
FindUsersInRole | Searches for users belonging to a specified role |
In our example we will code the methods and properties marked with * above. The remaining members will simply throw a "Not implemented" exception.
The complete source code of our custom membership provider can be found in the download (MyRolesProvider.cs). As an example CreateRole() method is given below:
public override void CreateRole(string roleName) { db.AddParameter("@ROLE", roleName); db.ExecuteNonQuery ("INSERT INTO ROLES(ROLENAME) VALUES(@ROLE)"); }
There are four test web forms provided along with the download - Default.aspx, Login.aspx, RoleManager.aspx and UserRoles.aspx. The first two test the membership provider and the later two test the roles provider. We use essentially the same Membership and Roles classes of ASP.NET. These classes in turn call our custom provides to get the job done.
In this article we saw how easy it is to develop your own providers for membership and role management. You can extend the application to suit your needs. You can also add more security features such as encryption and password strength.