Enable Password Resetting with Simple Membership in MVC 4
来源:互联网 发布:实时监测网速软件 编辑:程序博客网 时间:2024/05/16 15:39
http://www.itorian.com/2013/03/PasswordResetting.html
In this article you will learn how to enable password resetting (in case user forgot the password) with Simple Membership in MVC. User needs to type his username and system will check its existence, if found correct this will send an email containing dynamically generated URL with username and password reset token.
Before start with coding, let’s look at quick demo on YouTube.
This article is going to be quite long, so I will write it in two sections:
i) Sending Password Reset Information via Email
ii) Receiving Password Reset Information from URL
Both section will have step by step approach to help you understand which thing to do first.
Before following the steps given below, create a new MVC 4 Application with Internet Application template and then try running and creating a new user account, this will generate Simple Membership Database Tables.
i) Sending Password Reset Information via Email
Step 1
As you know, we don’t see an email field in a user registration form as well as in the membership database. But we can enable this using some quick changes in the application. Let’s make some changes in DB.
I added two new fields EmailId and Details.
Step 2
Now I want above newly added field’s information to be filled by user when he register on the website so, I need to update Register View Model as well as Register View. Here is the updated Register View Model.
public class RegisterModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
//new properties
[Required]
[Display(Name="Email ID")]
public string EmailId { get; set; }
[Required]
[Display(Name = "About Yourself")]
public string Details { get; set; }
}
See (highlighted above), I have added two new properties above to enable strongly typed for Register View. Here it is:
@model MvcMembership.Models.RegisterModel
@{
ViewBag.Title = "Register";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
<h2>Create a new account.</h2>
</hgroup>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary()
<fieldset>
<legend>Registration Form</legend>
<ol>
<li>
@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)
</li>
<li>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
</li>
<li>
@Html.LabelFor(m => m.ConfirmPassword)
@Html.PasswordFor(m => m.ConfirmPassword)
</li>
<li>
@Html.LabelFor(m => m.EmailId)
@Html.TextBoxFor(m => m.EmailId)
</li>
<li>
@Html.LabelFor(m => m.Details)
@Html.TextBoxFor(m => m.Details)
</li>
</ol>
<input type="submit" value="Register" />
</fieldset>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
I highlighted the changed made in above code. Now, when user will hit ‘Register’ button on Register view a POST request will happen containing UserName, Password, EmailId, Details.
Step 3
Now, I need to make some changes in POST version of Register controller.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { EmailId = model.EmailId, Details = model.Details});
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I highlighted the changes made in above code. You can see this controller will accept a ‘model’ of type RegisterModel that you can see in step 2.
Please note, this controller will do three things, create a new user account, login and redirect on Index view of Home controller.
So, user is registered now and there is email and details information up in my database.
Step 4
Now, we are ready to implement the password reset functionality for above modified application.
First let’s display a link to user on login page.
When user will click on above ‘Recover’ link, he will be redirect to a new view ‘ForgotPassword’, you need to create this view and its GET and POST methods.
Step 5
Before creating ‘ForgotPassword’ view you need GET and POST version action methods in controller, I will create both in AccountController.
So, the GET version of the action method is here.
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
And it will return following view page.
@{
ViewBag.Title = "Forgot Password";
}
<h2>Forgot Password</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<fieldset>
<legend>Forgot Password Form</legend>
<ol>
<li>
@Html.Label("User Name", new { @for = "UserName" })
@Html.TextBox("UserName")
<span style="color:red;">@TempData["Message"]</span>
</li>
</ol>
<input type="submit" value="Recover" />
</fieldset>
}
That’s it. Notice three things, a TextBox by name ‘UserName’, a <span> to display the message in red color, and a button (input) to submit the UserName with POST request.
So, the POST version of ‘ForgotPassword’ action method which accepts ‘UserName’ from POST request made by above view page is here.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(string UserName)
{
//check user existance
var user = Membership.GetUser(UserName);
if (user == null)
{
TempData["Message"] = "User Not exist.";
}
else
{
//generate password token
var token = WebSecurity.GeneratePasswordResetToken(UserName);
//create url with above token
var resetLink = "<a href='" + Url.Action("ResetPassword", "Account", new { un = UserName, rt = token }, "http") + "'>Reset Password</a>";
//get user emailid
UsersContext db = new UsersContext();
var emailid = (from i in db.UserProfiles
where i.UserName == UserName
select i.EmailId).FirstOrDefault();
//send mail
string subject = "Password Reset Token";
string body = "<b>Please find the Password Reset Token</b><br/>" + resetLink; //edit it
try
{
SendEMail(emailid, subject, body);
TempData["Message"] = "Mail Sent.";
}
catch (Exception ex)
{
TempData["Message"] = "Error occured while sending email." + ex.Message;
}
//only for testing
TempData["Message"] = resetLink;
}
return View();
}
In above action method with the received UserName from POST call I will check for username existence in DB, if not found will display ‘User Not exist.’ with the help of TempData. In case username matches, WebSecurity.GeneratePasswordResetToken will generate a password reset token and put it in the membership database for matching username. After generating password reset token it will generate a URL containing username and password reset token that will be sent via email [Read more in step 6]. Also note, I am using ResetPassword action method of Account controller that we have not implemented so far, you will see it later in this article. At the end I called a method ‘SendMail’ by passing emailed (grabbed from db with linq query), subject and body (containing generated URL) as a parameters.
Step 6
Now, I have URL (containing username and token) to send via email but we don’t have email in scope. So, I created an instance of UserProfile DB Model (you get this automatically when simple membership database comes-up) using UsersContext db = new UsersContext(), you can see it in above POST version of ‘ForgotPassword’ controller.
Here is the DbContext that we get for membership database.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
//newly added
//public DbSet<webpages_Membership> webpages_Memberships { get; set; }
}
In the Linq query you can see how I’m getting intellisense support for my EmailId and Details field also.
Actually, this is not default, I made some changes in UserProfile View Model, which is here.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
//new properties
public string EmailId { get; set; }
public string Details { get; set; }
}
I highlighted the changes made in above code.
So, far we sent the email containing URL to reset password. Now, user is going to click on the URL we sent via mail and we need to deal with that incoming GET call and verify them by matching username and token (by grabbed data from URL).
ii) Receiving Password Reset Information from URL
Step 1
We need a GET action method by name ‘ResetPassword’ in ‘Account’ controller because we sent this via mail (Read Setp 5 above). Let’s implement it.
[AllowAnonymous]
public ActionResult ResetPassword(string un, string rt)
{
UsersContext db = new UsersContext();
//TODO: Check the un and rt matching and then perform following
//get userid of received username
var userid = (from i in db.UserProfiles
where i.UserName == un
select i.UserId).FirstOrDefault();
//check userid and token matches
bool any = (from j in db.webpages_Memberships
where (j.UserId == userid)
&& (j.PasswordVerificationToken == rt)
//&& (j.PasswordVerificationTokenExpirationDate < DateTime.Now)
select j).Any();
if (any == true)
{
//generate random password
string newpassword = GenerateRandomPassword(6);
//reset password
bool response = WebSecurity.ResetPassword(rt, newpassword);
if (response == true)
{
//get user emailid to send password
var emailid = (from i in db.UserProfiles
where i.UserName == un
select i.EmailId).FirstOrDefault();
//send email
string subject = "New Password";
string body = "<b>Please find the New Password</b><br/>" + newpassword; //edit it
try
{
SendEMail(emailid, subject, body);
TempData["Message"] = "Mail Sent.";
}
catch (Exception ex)
{
TempData["Message"] = "Error occured while sending email." + ex.Message;
}
//display message
TempData["Message"] = "Success! Check email we sent. Your New Password Is " + newpassword;
}
else
{
TempData["Message"] = "Hey, avoid random request on this page.";
}
}
else
{
TempData["Message"] = "Username and token not maching.";
}
return View();
}
In above code, at very first you can see this method is accepting ‘un’ (which is username) and ‘rt’ (which is password reset token) from the URL.
Then, I created an instance of UserProfile DB Model that will allow me linq query with intellisense.
In the image given below you can see we have UserName in UserProfile Table and PasswordVerificationToken in webpages_Membership Table.
As you know we have UserName and Token (grabbed from URL). So, how will you match both, the best way is by using Stored Procedure but I don’t want to make this long article a book, lol. What I’m going to do is I will get UserId from UserProfile Table (look at first Linq query) and then will match the UserId and Token (look at second Linq query).
The second linq query I’m using ‘db.webpages_Memberships’ as DbSet, so don’t forget to implement it. Here is the DbSet.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
//newly added
public DbSet<webpages_Membership> webpages_Memberships { get; set; }
}
I highlighted the changes made in above code. Notice I have used ‘webpages_Memberships’ as a model, we need to implement this too, this section is completely new.
[Table("webpages_Membership")]
public class webpages_Membership
{
[Key]
public int UserId { get; set; }
public DateTime CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public bool IsConfirmed { get; set; }
public DateTime LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
public string Password { get; set; }
public DateTime PasswordChangeDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public DateTime PasswordVerificationTokenExpirationDate { get; set; }
}
Also, the second linq query will return Boolean (variable name is ‘any’) value because I have used .Any() with linq query to check ‘do you have any matching record’, if yes return ‘true’ if no return ‘false’ and show the not matched message immediately.
In case both matches Boolean will have ‘true’ value. Then a random password of length 6 will be generated using a method, given below.
private string GenerateRandomPassword(int length)
{
string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789!@$?_-*&#+";
char[] chars = new char[length];
Random rd = new Random();
for (int i = 0; i < length; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
And this generated password will saved in membership database with the help of WebSecurity.ResetPassword method that accepts token and new password. At the same time will get the user’s email id using linq query and send this newly generated password.
Okay, I’m done with everything now, but?
I have used ‘SendEmail’ method twice and the code is here, which is for those who don’t know how to send email.
private void SendEMail(string emailid, string subject, string body)
{
SmtpClient client = new SmtpClient();
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.EnableSsl = true;
client.Host = "smtp.gmail.com";
client.Port = 587;
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("xxxxx@gmail.com","xxxxx");
client.UseDefaultCredentials = false;
client.Credentials = credentials;
MailMessage msg = new MailMessage();
msg.From = new MailAddress("xxxxx@gmail.com");
msg.To.Add(new MailAddress(emailid));
msg.Subject = subject;
msg.IsBodyHtml = true;
msg.Body = body;
client.Send(msg);
}
Hope this helps.
0 0
- Enable Password Resetting with Simple Membership in MVC 4
- Enable weak password in Ubuntu
- Resetting Password for Ubuntu
- Using Membership in ASP.Net MVC 4 (转)
- MVC - Membership
- Resetting a forgotten MySql root password
- Resetting the Root Password: Unix Systems
- enable secret 和enable password
- Resetting The Buffer in SAP
- Simple Synchronizer Token with Spring MVC
- Enable Plain Text Password
- enable password 7
- Email Services in Salesforce with simple example
- A Simple MVC Setup In Node.JS
- Part 85 - Enable client side validation in asp.net mvc
- enable password和enable secret解析
- Resetting an Associative array in PL/SQL?
- service password-encryption,enable secret,enable password区别
- Shell parameter expansion
- kdump初探
- cgroups
- Email address validation using ASP.NET MVC data type attributes
- How do I add placeholder text from the model into a MVC view?
- Enable Password Resetting with Simple Membership in MVC 4
- Working with Roles in ASP.NET MVC 4+
- linux 实时时钟(RTC)驱动
- No Tipping +回溯+物理+贪心
- Display a view from another controller in ASP.NET MVC
- mini2440驱动分析之LCD
- 纯CSS气泡框实现方法探究
- How to add css class and id in @Html.TextBox mvc4 at the same time
- Hadoop2.4.1部署(完整版)