2010-01-23 93 views
1

Web應用程序從使用ASP.NET成員使用我自己的日誌系統,只是沒有像這樣標記一個用戶切換爲登錄:是否可以將.ASPXAUTH用於我自己的日誌記錄系統?

Session["UserId"] = User.Id 

是否可以存儲用戶ID在ASPXAUTH cookie中,捎帶加密,而不是使用標準會話?

目標是讓登錄狀態的持續時間超過一個會話並在瀏覽器和服務器重新啓動時保持不變。

+1

你介意分享你放棄ASP.NET Membership的原因嗎? – Greg 2010-01-26 17:04:37

+1

當然,格雷格,我們喜歡簡單的數據庫關係,當保持整個用戶配置文件在一個簡單的類。此外,我們使用的是OpenID,因此使用ASP.NET Membership已經是一種破解,並且允許每個用戶使用一個以上的OpenID標識符幾乎不可能。 – Pablo 2010-01-28 01:43:15

+0

聽起來很酷,謝謝。 – Greg 2010-01-29 21:50:10

回答

5

更新:提供的原始答案是一個使用MembershipProvider的項目,它在答案中進行了解釋。我,提問者,我沒有使用它,所以我的問題的答案略有不同,但從這個答案中提取。我將我的答案放在底層,讓任何關心和留下最初的逐字記錄的人都能回答,因爲它包含了很多價值。


是的,您可以將FormsAuthentication用於您自己的策略。儘管asp.net數據庫結構不適合您,但您可以提供一個MembershipProvider的簡單實現,以允許使用Membership基礎結構。這兩個功能沒有結婚,所以你可以決定適合你的東西。

記住你的問題和一些評論,這裏是一個可運行的例子,它是如何簡單地利用提供者模型而不結合默認實現和db模式。

爲了自己的目的使用表單認證很簡單。您只需提供身份驗證並設置您自己的票證(cookie)。

使用自定義成員身份幾乎同樣簡單。您可以實現儘可能少的或儘可能多的提供程序,以支持您希望使用的asp.net基礎結構功能。

例如在下面的示例中,我展示了在登錄過程中,您可以簡單地處理登錄控件上的事件以驗證憑據並設置票證。完成。

但我還會展示如何利用提供者模型和實現自定義成員資格提供程序可以產生更強,更乾淨的代碼。雖然我們位於自定義成員資格提供程序中,但我實現了支持使用成員資格子系統來提供對用戶元數據的輕鬆訪問而不需要編寫自己的基礎結構所需的最低限度要求。

只需將這些文件放入一個空白項目即可。

的web.config


<?xml version="1.0"?> 
<configuration> 
    <system.web> 
    <compilation debug="true"/> 
    <authorization> 
     <deny users="?"/> 
    </authorization> 
    <authentication mode="Forms"/> 
    <!-- 
    optional but recommended. reusing the membership infrastructure via custom provider divorces 
    you from the aspnetdb but retains all of the baked in infrastructure which you do not have to 
    develop or maintain 
    --> 
    <membership defaultProvider="mine"> 
     <providers> 
     <add name="mine" type="CustomAuthRepurposingFormsAuth.MyMembershipProvider"/> 
     </providers> 
    </membership> 
    </system.web> 
</configuration> 

站點1。掌握


<%@ Master Language="C#" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
    <title></title> 
    <asp:ContentPlaceHolder ID="head" runat="server"> 
    </asp:ContentPlaceHolder> 
</head> 
<body> 
    <form id="form1" runat="server"> 
    <div> 
     <asp:LoginName ID="LoginName1" runat="server" /> 
     <asp:LoginStatus ID="LoginStatus1" runat="server" /> 
     <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server"> 
     </asp:ContentPlaceHolder> 
    </div> 
    </form> 
</body> 
</html> 

的Login.aspx


<%@ Page Title="" Language="C#" MasterPageFile="Site1.Master" %> 
<%@ Import Namespace="CustomAuthRepurposingFormsAuth" %> 
<script runat="server"> 

    /* 
    * If you don't want to use a custom membership provider to authenticate 
    * simply place your logic in the login control's handler and remove the 
    * membership element from config. It would have to take a very very 
    * compelling edge case to motivate me to not use a custom membership provider. 
    * 
    */ 

    //protected void Login1_Authenticate(object sender, AuthenticateEventArgs e) 
    //{ 
    // // perform mindbendingly complex authentication logic 
    // e.Authenticated = Login1.UserName == Login1.Password; 
    //} 


    /* 
    * set your cookie and you are golden 
    */ 
    void Authenticated(object sender, EventArgs e) 
    { 
     // this is an arbitrary data slot you can use for ??? 
     // keep cookie size in mind when using it. 
     string userData = "arbitraryData"; 
     Response.Cookies.Add(TicketHelper.CreateAuthCookie(Login1.UserName, userData, Login1.RememberMeSet /*persistent cookie*/)); 
    } 

</script> 

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server"> 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> 
    <asp:Login ID="Login1" runat="server" OnLoggedIn="Authenticated" > 
    </asp:Login> 
    username==password==authenticated. <br />e.g.: uid: me, pwd:me 
</asp:Content> 

Default.aspx的


<%@ Page Title="" Language="C#" MasterPageFile="Site1.Master" %> 

<%@ Import Namespace="System.Security.Principal" %> 
<%@ Import Namespace="CustomAuthRepurposingFormsAuth" %> 

<script runat="server"> 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     /* 
     * you get this for free from asp.net 
     */ 

     HttpContext page = HttpContext.Current; 

     IIdentity identity = page.User.Identity; 
     string username = identity.Name; 
     bool authenticate = identity.IsAuthenticated; 
     // or use the Request.IsAuthenticated convenience accessor 

     /* 
     * you get this really cheap from forms auth 
     * 
     * cost: validating credentials and setting your own ticket 
     */ 

     // this page is protected by formsauth so the identity will actually 
     // be a FormsIdentity and you can get at the user data. 
     // UserData is an appropriate place to store _small_ amounts of data 
     var fIdent = (FormsIdentity)identity; 
     string userData = fIdent.Ticket.UserData; 


     // so, using only forms auth this is what you have to work with 
     LblAuthenticated.Text = page.User.Identity.IsAuthenticated.ToString(); 
     LblUserId.Text = page.User.Identity.Name; 
     LblUserData.Text = userData; 

     /* 
     * this is an example of using a custom membership provider and subclassing the 
     * MembershipUser class to take advantage of the established mature infrastructure 
     * 
     * this is entirely optional, you can delete the Membership section in web.config 
     * and delete MyMembershipProvider and MyMembershipUser and just use the authentication. 
     * 
     */ 

     // get the custom field 
     string myCustomField = ((MyMembershipUser)Membership.GetUser()).MyCustomField; 
     LblMembership.Text = myCustomField; 
    }   
</script> 

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server"> 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> 
    <br /> 
    Authenticated:<asp:Label ID="LblAuthenticated" runat="server" Text=""></asp:Label><br /> 
    UserId:<asp:Label ID="LblUserId" runat="server" Text=""></asp:Label><br /> 
    UserData:<asp:Label ID="LblUserData" runat="server" Text=""></asp:Label><br /> 
    <br /> 
    Membership User Custom Field:<asp:Label ID="LblMembership" runat="server" Text=""></asp:Label><br /> 
</asp:Content> 

CustomAuthClasses.cs


using System; 
using System.Web; 
using System.Web.Security; 

namespace CustomAuthRepurposingFormsAuth 
{ 
    public static class TicketHelper 
    { 
     /// <summary> 
     /// 
     /// </summary> 
     /// <param name="userName"></param> 
     /// <param name="userData">be mindful of the cookie size or you will be chasing ghosts</param> 
     /// <param name="persistent"></param> 
     /// <returns></returns> 
     public static HttpCookie CreateAuthCookie(string userName, string userData, bool persistent) 
     { 
      DateTime issued = DateTime.Now; 
      // formsAuth does not expose timeout!? have to hack around the 
      // spoiled parts and keep moving.. 
      HttpCookie fooCookie = FormsAuthentication.GetAuthCookie("foo", true); 
      int formsTimeout = Convert.ToInt32((fooCookie.Expires - DateTime.Now).TotalMinutes); 

      DateTime expiration = DateTime.Now.AddMinutes(formsTimeout); 
      string cookiePath = FormsAuthentication.FormsCookiePath; 

      var ticket = new FormsAuthenticationTicket(0, userName, issued, expiration, true, userData, cookiePath); 
      return CreateAuthCookie(ticket, expiration, persistent); 
     } 

     public static HttpCookie CreateAuthCookie(FormsAuthenticationTicket ticket, DateTime expiration, bool persistent) 
     { 
      string creamyFilling = FormsAuthentication.Encrypt(ticket); 
      var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, creamyFilling) 
          { 
           Domain = FormsAuthentication.CookieDomain, 
           Path = FormsAuthentication.FormsCookiePath 
          }; 
      if (persistent) 
      { 
       cookie.Expires = expiration; 
      } 

      return cookie; 
     } 
    } 

    /// <summary> 
    /// This is an example of inheriting MembershipUser to 
    /// expose arbitrary data that may be associated with your 
    /// user implementation. 
    /// 
    /// You may repurpose existing fields on the base and add your own. 
    /// Just perform a cast on the MembershipUser returned from your 
    /// MembershipProvider implementation 
    /// </summary> 
    public class MyMembershipUser : MembershipUser 
    { 
     public MyMembershipUser(string providerName, string name, object providerUserKey, string email, 
           string passwordQuestion, string comment, bool isApproved, bool isLockedOut, 
           DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, 
           DateTime lastPasswordChangedDate, DateTime lastLockoutDate) 
      : base(
       providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut, 
       creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate) 
     { 
     } 

     protected MyMembershipUser() 
     { 
     } 

     // e.g. no desire to use Profile, can just add data 
     // say, from a flat record containing all user data 
     public string MyCustomField { get; set; } 
    } 

    /// <summary> 
    /// At the most basic level, implementing a MembershipProvider allows you to 
    /// reuse established framework code. In this case, we just provide services 
    /// for the Login control and user identification via Membership subsystem. 
    /// </summary> 
    public class MyMembershipProvider : MembershipProvider 
    { 
     #region Minimum implementation in order to use established authentication and identification infrastructure 

     /// <summary> 
     /// You can just do this in the login logic if you do not want 
     /// leverage framework for membership user access 
     /// </summary> 
     public override bool ValidateUser(string username, string password) 
     { 
      return username == password; 
     } 


     public override MembershipUser GetUser(string username, bool userIsOnline) 
     { 
      /* 
      * Simulate going to the DB to get the data 
      */ 

      // membership user non nullable fields, repurpose or use 
      // implied null value e.g DateTime.MinValue; 

      var createdDate = new DateTime(2009, 10, 25); 
      var lastLogin = new DateTime(2009, 10, 25); 
      var lastActivity = new DateTime(2009, 10, 25); 
      var lastPasswordChange = new DateTime(2009, 10, 25); 
      var lastLockoutDate = new DateTime(2009, 10, 25); 

      object providerUserKey = 3948; // e.g. user primary key. 


      /* 
      * build your custom user and send it back to asp.net 
      */ 

      // need to use the full constructor to set the username and key 
      var user = new MyMembershipUser(Name, username, providerUserKey, null, null, null, true, false, createdDate, 
              lastLogin, 
              lastActivity, lastPasswordChange, lastLockoutDate) 
          { 
           MyCustomField = "Hey" 
          }; 

      return user; 
     } 

     #endregion 

     #region Optional implementations depending on the framework features you would like to leverage. 

     public override bool EnablePasswordRetrieval 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override bool EnablePasswordReset 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override bool RequiresQuestionAndAnswer 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override string ApplicationName 
     { 
      get { throw new NotImplementedException(); } 
      set { throw new NotImplementedException(); } 
     } 

     public override int MaxInvalidPasswordAttempts 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override int PasswordAttemptWindow 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override bool RequiresUniqueEmail 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override MembershipPasswordFormat PasswordFormat 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override int MinRequiredPasswordLength 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override int MinRequiredNonAlphanumericCharacters 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override string PasswordStrengthRegularExpression 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) 
     { 
      throw new NotImplementedException(); 
     } 


     public override MembershipUser CreateUser(string username, string password, string email, 
                string passwordQuestion, string passwordAnswer, bool isApproved, 
                object providerUserKey, out MembershipCreateStatus status) 
     { 
      throw new NotImplementedException(); 
     } 

     public override bool ChangePasswordQuestionAndAnswer(string username, string password, 
                  string newPasswordQuestion, string newPasswordAnswer) 
     { 
      throw new NotImplementedException(); 
     } 

     public override string GetPassword(string username, string answer) 
     { 
      throw new NotImplementedException(); 
     } 

     public override bool ChangePassword(string username, string oldPassword, string newPassword) 
     { 
      throw new NotImplementedException(); 
     } 

     public override string ResetPassword(string username, string answer) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void UpdateUser(MembershipUser user) 
     { 
      throw new NotImplementedException(); 
     } 

     public override bool UnlockUser(string userName) 
     { 
      throw new NotImplementedException(); 
     } 


     public override string GetUserNameByEmail(string email) 
     { 
      throw new NotImplementedException(); 
     } 

     public override bool DeleteUser(string username, bool deleteAllRelatedData) 
     { 
      throw new NotImplementedException(); 
     } 

     public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) 
     { 
      throw new NotImplementedException(); 
     } 

     public override int GetNumberOfUsersOnline() 
     { 
      throw new NotImplementedException(); 
     } 

     public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, 
                   out int totalRecords) 
     { 
      throw new NotImplementedException(); 
     } 

     public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, 
                    out int totalRecords) 
     { 
      throw new NotImplementedException(); 
     } 

     #endregion 
    } 
} 

解決實際使用(如在使用OpenID的ASP.NET MVC項目):

我有我使用的AccountController登錄和註銷用戶,這些方法在那裏。

#region Methods to log in a user. 
/// <summary> 
/// Create the auth cookie in the same way it is created my ASP.NET Membership system, hopefully lasting for more than 20 minutes. 
/// 
/// For more information check out http://stackoverflow.com/questions/2122831/is-it-possible-to-use-aspxauth-for-my-own-logging-system 
/// </summary> 
/// <param name="userId">Id of the user that is logged in</param> 
/// <returns>Cookie created to mark the user as authenticated.</returns> 
private static HttpCookie CreateAuthCookie(int userId) { 
    DateTime issued = DateTime.Now; 
    // formsAuth does not expose timeout!? have to hack around the spoiled parts and keep moving.. 
    HttpCookie fooCookie = FormsAuthentication.GetAuthCookie("foo", true); 
    int formsTimeout = Convert.ToInt32((fooCookie.Expires - DateTime.Now).TotalMinutes); 

    DateTime expiration = DateTime.Now.AddMinutes(formsTimeout); 

    var ticket = new FormsAuthenticationTicket(0, userId.ToString(), issued, expiration, true, "", FormsAuthentication.FormsCookiePath); 
    return CreateAuthCookie(ticket, expiration, true); 
} 

/// <summary> 
/// Create an auth cookie with the ticket data. 
/// </summary> 
/// <param name="ticket">Ticket containing the data to mark a user as authenticated.</param> 
/// <param name="expiration">Expriation date for the cookie.</param> 
/// <param name="persistent">Whether it's persistent or not.</param> 
/// <returns>Cookie created to mark the user as authenticated.</returns> 
private static HttpCookie CreateAuthCookie(FormsAuthenticationTicket ticket, DateTime expiration, bool persistent) { 
    string encryptedAuthData = FormsAuthentication.Encrypt(ticket); 
    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedAuthData) { 
    Domain = FormsAuthentication.CookieDomain, 
    Path = FormsAuthentication.FormsCookiePath 
    }; 
    if (persistent) { 
    cookie.Expires = expiration; 
    } 

    return cookie; 
} 

/// <summary> 
/// Expire the authentication cookie effectively loging out a user. 
/// </summary> 
private void ExpireAuthCookie() { 
    var cookie = new HttpCookie(FormsAuthentication.FormsCookieName); 
    cookie.Expires = DateTime.Now.AddDays(-1); 
    Response.Cookies.Add(cookie); 
} 
#endregion 
+0

我如何做到這一點,而不使用通常的會員設置? – Pablo 2010-02-18 08:23:26

+0

@J。 PabloFernández,當然,實際上你可以像使用你自己的會員實施一樣使用表單認證子系統。如果你想顛覆會員子系統很簡單。我現在正在做一個樣本。 – 2010-02-18 13:00:47

+0

感謝Sky Sanders,很多! – Pablo 2010-02-18 17:09:26

1

ASPXAUTH cookie將認證令牌與會話完全分開,這是它使用的原因之一。如果您的系統使用會話,那麼它與身份驗證嘗試執行的表單確實存在衝突。

如果一切都在會話中,那麼假設會話是安全的,那麼實際上根本不需要身份驗證票證。

+0

我不再使用表單身份驗證。我只是想知道是否可以在ASPXAUTH cookie而不是會話中保持登錄狀態。 – Pablo 2010-01-23 10:38:45

+0

登錄狀態基本上由cookie的存在來控制。如果它不在那裏,它就不會登錄。使用表單身份驗證令牌而不是普通的cookie並不能提供更多信息。即使如此,如果所有內容都在會話中,那麼肯定會話會記錄登錄或退出? – blowdart 2010-01-23 11:04:10

+0

用戶是否登錄不是僅由cookie的存在來控制,否則任何人都只會創建一個cookie並登錄。該cookie的內容非常重要。 .ASPXAUTH顯然將數據存儲在加密的cookie中,並持續時間長達我想要的時間。 Session會在cookie中存儲一個id並處理數據,如果進程死亡,它通常會持續20分鐘並死亡。我想要我的登錄狀態數據的ASPXAUTH行爲。 – Pablo 2010-01-23 11:49:24

相關問題