问题
Ok, So I am learning ASP.Net MVC 5 by doing small projects. I am implementing forgot password functionality. Here is the flow:
- User clicks on forgets the password link.
- Redirects to page where User enters the email.
- Receives a token and redirects to a page to enter the token received
- Gets redirected to "password" and "confirm password" page
Now all these pages have their separate URL and pages should open in that order. But If I type the URL which is required for may be 3rd step (enter the token) or URL of 4th Step (enter the new password) in my browser link I can open the corresponding page. But I do not want that. If someone is not following steps 1 to 4 then they cannot directly open any out of sequence URL. How to achieve above functionality.
Note: If we add a [Authorize]
attribute to our controller action then no matter which URL we access in browser we always get redirected to login page (provided not already logged in). Can I achieve something like above. So, if someone tries to open /VerifyToken
Page directly then he should be redirected to page /SendEmailForReceiveingToken
.
EDIT 1
Ok, Here is the thing. In order to identify the right person, I am using the token. But in order to check the token field in my Database I want to uniquely know the user. For that I am utilizing the email Id. So basically I query the DB with email ID to find the user and then check the token set in that row. But if someone directly goes to verify token, then I will not be able to know his email Id. As of now I am passing the email as hidden field among token page
and resetpassword page
EDIT 2
So at 12:00 AM user got the token. But he did not go ahead with the "Reset Password". He comes after 2 mins and directly goes to /VerifyToken
page and enters the token. So now I do not have access to the email id. How can I verify the token now. I do not want to query entire token column and match the corresponding row.
回答1:
There's nothing wrong with an arbitrary user opening for example page 3 where he is supposed to enter a valid token. If he doesn't have received such valid token before and you have proper validation of the token on the next step, then this user cannot do anything useful. You are over-complicating the design.
Just put proper validation on the step where the actual password reset happens - verify that a valid token has been entered. It doesn't matter the page from which this token has been submitted as soon as the token is valid.
回答2:
You are correct in thinking that you need email address in order to verify the token. However you do not need to establish a rigid flow for this purpose.
You can just insert email address to password reset url as a query parameter. Example url:
http://www.example.com/resetpassword/ZXhhbXBsZQ?email=example@example.com
Then you query tokens of the user with the designated email.
Or you can ask for user email at step 3 or 4. If you take a look at ASP.NET Identity based default project Visual Studio creates, that's what you'll see. Password form also asks the user to enter his email.
Or alternatively you can use a more advanced token, in which it is possible to embed information. For example it is possible to embed user principals into a JWT token. However, my personal opinion this is an overkill for password reset. One apparent downside is JWT tokens and thus your password reset URLs will be much longer.
回答3:
Assuming your tokens are actually unique, you don't actually need to know the email address / user name for the user. The token acts as a (temporary) unique ID for a user record, so you can just persist that token within the "enter your new password" view, and use that within your reset logic:
public bool UpdatePasswordForToken(string token, string newPassword)
{
bool success = false;
var user = Context.Users.SingleOrDefault(u => u.ResetToken.Equals(token, StringComparison.OrdinalIgnoreCase));
if (user != null)
{
var password = Crypto.HashPassword(newPassword);
if (!string.IsNullOrWhiteSpace(password))
{
user.Password = password;
user.ResetToken = null;
Context.SaveChanges();
success = true;
}
}
return success;
}
This assumes that you don't just log the user in when they complete the password reset. I redirect to the login page (with a success message). Even if someone managed to guess a valid reset token (which means the tokens aren't really unique), they would need to know which user name they just reset, in order to log in as the (now compromised) user.
If you're really set on a user only following your workflow, the obvious answer seems to be checking the Request.UrlReferrer
value, and bouncing the user from the current action if it's not the action you wanted to proceed this action. UrlReferrer isn't bulletproof, though, so I would recommend just simplifying your workflow like I note above.
来源:https://stackoverflow.com/questions/43575329/how-to-prevent-user-from-accessing-certain-url-directly-in-asp-net-mvc-5