我有一个使用asp.net登录控件的Web应用程序。另外,我还使用密码恢复控件来恢复用户的密码。一旦用户在恢复控制中输入了他们的详细信息,包含验证URL的电子邮件将被发送到用户的电子邮件地址。点击URL后,它会将用户引导到我的Web应用程序的UserProfile中,在其中,它允许用户更改其密码。
现在的问题是,因为我将访问规则设置为UserProfile.aspx以拒绝匿名用户,当我从URL重定向到UserProfile.aspx页面时,它将我引导到LoginPage(系统将我识别为一个匿名用户)。
为什么会这样?在点击URL(包括所有用户信息)后,我可以在任何地方指向userprofile页面吗?
网址如下所示:
http://localhost:1039/Members/UserProfile.aspx?ID=56f74cc7-7680-4f1b-9207-0ab8dad63cad
网址的最后一部分实际上是userId。
以下是userprofile aspx的代码:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:ASPNETDBConnectionString1 %>"
SelectCommand="SELECT aspnet_Membership.Email, Details.CustName, Details.CustNum, Details.CustRole, Details.CustStatus, Details.PName, Details.PEmail, Details.PRole, Details.WedDate, aspnet_Users.UserName, Details.UserId FROM Details INNER JOIN aspnet_Membership ON Details.UserId = aspnet_Membership.UserId INNER JOIN aspnet_Users ON aspnet_Membership.UserId = aspnet_Users.UserId WHERE (Details.UserId = @UserId)"
UpdateCommand="update Details SET CustName = @CustName, CustNum = @CustNum, CustRole = @CustRole, CustStatus = @CustStatus, PName = @PName, PEmail = @PEmail, PRole = @PRole, WedDate = @WedDate WHERE [UserId] = @UserId
Update aspnet_Membership Set Email= @email WHERE [UserId] = @UserId"
DeleteCommand= "DELETE FROM Details WHERE UserId = @UserId;">
<DeleteParameters>
<asp:ControlParameter ControlID="lblHidden" Name="UserId" PropertyName="Text"
Type="String" />
</DeleteParameters>
<SelectParameters>
<asp:ControlParameter ControlID="lblHidden" Name="UserId" PropertyName="Text" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="CustName" />
<asp:Parameter Name="CustNum" />
<asp:Parameter Name="CustRole" />
<asp:Parameter Name="CustStatus" />
<asp:Parameter Name="PName" />
<asp:Parameter Name="PEmail" />
<asp:Parameter Name="PRole" />
<asp:Parameter Name="WedDate" />
<asp:Parameter Name="UserId" />
<asp:Parameter Name="email" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False"
DataSourceID="SqlDataSource1" Height="50px" Width="125px">
<Fields>
<asp:BoundField DataField="Email" HeaderText="Email" SortExpression="Email" />
<asp:BoundField DataField="CustName" HeaderText="CustName"
SortExpression="CustName" />
<asp:BoundField DataField="CustNum" HeaderText="CustNum"
SortExpression="CustNum" />
<asp:BoundField DataField="CustRole" HeaderText="CustRole"
SortExpression="CustRole" />
<asp:BoundField DataField="CustStatus" HeaderText="CustStatus"
SortExpression="CustStatus" />
<asp:BoundField DataField="PName" HeaderText="PName" SortExpression="PName" />
<asp:BoundField DataField="PEmail" HeaderText="PEmail"
SortExpression="PEmail" />
<asp:BoundField DataField="PRole" HeaderText="PRole" SortExpression="PRole" />
<asp:BoundField DataField="WedDate" HeaderText="WedDate"
SortExpression="WedDate" />
<asp:BoundField DataField="UserName" HeaderText="UserName"
SortExpression="UserName" />
<asp:BoundField DataField="UserId" HeaderText="UserId"
SortExpression="UserId" />
<asp:CommandField ShowEditButton="True" />
</Fields>
</asp:DetailsView>
<asp:Label ID="lblHidden" runat="server" Text="Label" Visible="False"></asp:Label>
<asp:Button ID="btnDelete" runat="server" onclick="btnDelete_Click"
Text="Delete" />
这是背后的代码:
protected void Page_Load(object sender, EventArgs e)
{
MembershipUser currentUser = Membership.GetUser();
lblHidden.Text = currentUser.ProviderUserKey.ToString();
}
protected void SqlDataSource1_Selecting(object sender, SqlDataSourceSelectingEventArgs e)
{
// Get a reference to the currently logged on user
MembershipUser currentUser = Membership.GetUser();
// Determine the currently logged on user's UserId value
// Assign the currently logged on user's UserId to the @UserId parameter
//access the parameter value using e.Command.Parameters
//programmatically set the @UserId:
e.Command.Parameters["@UserId"].Value = currentUser.ProviderUserKey.ToString();
}
protected void btnDelete_Click(object sender, EventArgs e)
{
SqlConnection connection = new SqlConnection();
connection.ConnectionString = ConfigurationManager.ConnectionStrings["ASPNETDBConnectionString1"].ConnectionString;
SqlCommand cmd = new SqlCommand();
SqlCommand cmd1 = new SqlCommand();
string userId = lblHidden.Text;
cmd.Connection = connection;
cmd.CommandText = "DELETE FROM Details WHERE UserId ='" + userId + "'";
cmd1.Connection = connection;
cmd1.CommandText = "DELETE FROM aspnet_Membership WHERE UserId ='" + userId + "'";
connection.Open();
cmd.ExecuteNonQuery();
cmd1.ExecuteNonQuery();
connection.Close();
Response.Redirect("Home.aspx");
}
其次,有什么方法可以设置URL的到期日期吗?如果第二次点击该URL,则不会将用户重定向到任何位置。我看到很多帖子,大多数都建议在数据库中添加一列。有没有其他方法我可以设置到期而不触及数据库?
答案 0 :(得分:5)
考虑更改密码链接的单独页面。让此页面采用唯一标识符。此标识符应仅工作一次,具有到期日期,并且特定于该用户。公开此页面:
<location path="changepassword.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
您需要在用户的某处存储唯一标识符。如果您不想影响当前架构,可以创建一个新表:
PK | Identifier | UserID | expires
1 | abcd | ffffffff-ffff-ffff-ffff-ffffffffffff | 16-jul-2012 18:26
请求页面时,如果标识符已过期,则不允许页面正常运行。密码更改后,使标识符无效 - 删除标识符,或将过期日期设置为过去的标识日期(例如现在)。
答案 1 :(得分:2)
这不是对所提问题的直接回应,而是对构造密码重置工具的更一般性评论......
编码此功能时,我会做一些不同的事情。
访问个人资料页面或只是更改密码?
首先,如果用户只需要更改密码,则只需更改密码页面即可访问用户个人资料页面。我会直接将它们带到“更改密码”页面,并使其特定于密码重置功能,从而匿名提供,没有任何问题。
为什么不对其进行身份验证,然后将其发送到个人资料页面?
另一种选择是通过匿名访问来实现自动登录而不是乱七八糟。该URL会导致登录页面自动记录给定用户(毕竟您拥有所有用户详细信息)。然后他们可以被重定向到个人资料页面,他们不再是匿名的,所以一切都很好。
纯文本用户ID的问题
您需要使用更好的令牌。如果有人可以找到另一个用户的用户ID(如果您点击查看用户公开个人资料,可能会显示在网址中),那么他们可以更改该人员的密码。我确定你知道这并不酷。
我使用什么代替明文用户ID?
随机代币
围绕此方法的两种方法是创建存储在数据库中的随机令牌,然后在加载页面时验证它们。这当然使得当你想要使它们无效时,只需更改数据库中的某些内容就可以很容易地使它们无效。您可以在DB中存储随机令牌(我可能会使用长度约为16个字符的base64编码字符串 - 实际上是96位随机性)以及任何必要的信息(例如,用户ID,创建日期(或到期日期))等)。如果您希望它成为一次使用,您可以在DB验证一次后清除令牌(或标记一个字段,说明它已被使用或任何其他替代品)。
加密令牌
不涉及触摸数据库的更安全的方法是创建可以传递给用户的加密令牌。此令牌可以包含userID和创建令牌的时间(以及您感觉到的任何其他信息),并且会被放入电子邮件中并被遗忘。因为它是回传时的加密数据而不是随机令牌,而不是通过数据库进行验证,所以您只需解密给定的令牌以便用户和令牌的年龄。
如果你想在没有访问数据库的情况下使用它们有点棘手但我可能会将生成的令牌存储在服务器应用程序字典中,如果令牌不在那里则无效并且一旦使用它就将其从存储的列表中删除。您还要定期清理旧令牌,以确保您不会存储太多垃圾。这种方法还有一个缺点,即如果重新启动应用程序,您将清除令牌列表,从而使它们全部无效。如果你需要它们只是一次拍摄,这只是一个问题。就个人而言,我会让他们在你认为适合单一令牌的任何时间范围内重置他们想要的密码。 :)
有关原始方案中明文用户ID的另一个警告
我无法强调你需要多么谨慎,只需将用户标识放在网址中的明文作为传递的唯一信息。当有人发现另一个人的用户ID时,他们的帐户会被有效地泄露。他们只需要将那些人的用户ID放入网址,他们就可以更改密码。
即使你没有任何地方暴露其他人的用户ID,你也必须100%确定你将来不会在实践中无法保证。
答案 2 :(得分:1)
您可以尝试通过在<configuration>
标记内的web.config中添加以下内容,允许匿名用户访问您的userprofile页面。
<location path="userProfile.aspx">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>