如何手动创建身份验证cookie而不使用默认方法?

58

使用 FormsAuthentication 时我们编写这样的代码:

 if (IsValidUser())
 {
      FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
      FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie); 
 }
  1. 如何手动创建身份验证cookie而不是编写 FormsAuthentication.SetAuthCookie(userName, createPersistentCookie)

  2. 如何将登录页面的重定向URL存储到字符串变量中而不是编写FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie)

2个回答

97

这就是你需要的。当你使用内置在FormsAuthentication中的更高级别的方法时,ASP.NET会为此负责,但是在低级别上,需要创建一个身份验证 cookie。

if (Membership.ValidateUser(username, password))
{  
  // sometimes used to persist user roles
  string userData = string.Join("|",GetCustomUserRoles());

  FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
    1,                                     // ticket version
    username,                              // authenticated username
    DateTime.Now,                          // issueDate
    DateTime.Now.AddMinutes(30),           // expiryDate
    isPersistent,                          // true to persist across browser sessions
    userData,                              // can be used to store additional user data
    FormsAuthentication.FormsCookiePath);  // the path for the cookie

  // Encrypt the ticket using the machine key
  string encryptedTicket = FormsAuthentication.Encrypt(ticket);

  // Add the cookie to the request to save it
  HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
  cookie.HttpOnly = true; 
  Response.Cookies.Add(cookie);

  // Your redirect logic
  Response.Redirect(FormsAuthentication.GetRedirectUrl(username, isPersistent));
}

我不确定为什么您想要在此处执行自定义操作。如果您想要更改用户数据存储的实现方式以及用户身份验证方法,则最佳做法是创建自定义的MembershipProvider。自己编写解决方案并踩入身份验证 cookie 可能会导致软件中引入安全漏洞的高概率。

我不理解您的第二部分。如果您想返回用户到登录之前正在访问的页面,则只需要调用FormsAuthentication.GetRedirectUrl。否则,您可以根据需要执行任何操作,在配置中重定向到 URL。

通常,要读取 FormsAuthentication cookie,您需要在 HttpModule 或 Global.asax 中挂钩 AuthenticateRequest 事件,并设置用户 IPrinciple 上下文。

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

        // If caching roles in userData field then extract
        string[] roles = authTicket.UserData.Split(new char[]{'|'});

        // Create the IIdentity instance
        IIdentity id = new FormsIdentity( authTicket );

        // Create the IPrinciple instance
        IPrincipal principal = new GenericPrincipal(id, roles);

        // Set the context user 
        Context.User = principal;
    }
}

3
我已更新以展示如何读取cookie。不能从客户端设置此cookie,因为这将带来安全风险,并且无法执行需要服务器端密钥的加密。身份验证cookie应始终为HttpOnly。唯一的方法是进行AJAX请求,并让cookie在服务器端设置,在这种情况下,您需要确保通过SSL传递任何凭据。 - TheCodeKing
@TheCodeKing:你是如何在这里设置“HttpOnly”标志的? - escist
2
在保存Cookie实例之前,您可以设置HttpOnly。已更新以反映最佳实践 :) - TheCodeKing
1
这真是帮了大忙,谢谢。我唯一想做的改变就是从web.config获取expiryDate超时分钟数。将DateTime.Now.AddMinutes(30)改为DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes) - LawMan
我喜欢LawMan的代码来添加FormsAuthentication中的分钟,但是从TimeSpan的分钟中添加分钟是多余的转换。我们可以使用这个:DateTime.Now.Add(FormsAuthentication.Timeout)。但这也不是什么大问题。而且将其转换为分钟再转回去也没有什么损害,只是多了几个CPU使用周期。 - Shawn Kovac
显示剩余4条评论

-8

有关此帖子投票负数数量的答案更新, 创建包含用户信息的 cookie 的正确方法如下:

在登录页面加载时对 cookie 进行验证,

if (HttpContext.Current.User.Identity.IsAuthenticated)

在经过身份验证的用户登录期间创建Cookie,

 FormsAuthentication.SetAuthCookie(txtUserName.Text.Trim(), true);
 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
    1,
    txtUserName.Text.Trim(), 
    DateTime.Now,
   (chkRemember.Checked) ? DateTime.Now.AddHours(6) : DateTime.Now.AddHours(2),// Specify timelimit as required
   true,
   string.Empty,                                                
   FormsAuthentication.FormsCookiePath);  
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.Expires = (chkRemember.Checked) ? DateTime.Now.AddHours(6) : DateTime.Now.AddHours(2);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);

以下是一个被踩的答案 - 原因是在 cookie 中添加了加密密码。
另一种创建 cookie 的方法,
HttpCookie toolCookie = new HttpCookie("xyz");
toolCookie["UserName"] = userName;
toolCookie["Password"] = StringCipher.Encrypt(password, "#!");
toolCookie.Expires = DateTime.Now.AddMinutes(chkRemember.Checked ? 30 : -30);
Request.Cookies.Add(toolCookie);

参考

获取现有 cookie 详细信息

HttpCookie user = Request.Cookies["xyz"];
if(user != null)
 {
  string username = user["UserName"];
  string password = user["Password"] != null ? StringCipher.Decrypt(user["Password"], "#!")
 }

这里Datasecurity是一个静态类。

加密和解密函数加密和解密


16
永远不要将密码放在Cookie中。这会破坏安全Cookie的全部作用。 - undefined
你能为我建议一个最佳解决方案吗? - Pranesh Janarthanan
你能检查一下这个例子吗?http://www.aspsnippets.com/Articles/Implement-Remember-Me-functionality-using-CheckBox-ASPNet.aspx - Pranesh Janarthanan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接