2010-01-23 12 views
1

Для веб-приложения Я переключился с помощью ASP.NET Членство в использовании мой собственный журнал в системе, которая просто делает что-то вроде этого, чтобы пометить пользователя, как войти в систему:Можно ли использовать .ASPXAUTH для моей собственной системы регистрации?

Session["UserId"] = User.Id 

Можно ли хранить идентификатор пользователя в cookie ASPXAUTH, используя его шифрование вместо использования стандартного сеанса?

Цель состоит в том, чтобы зарегистрированное состояние продолжалось дольше, чем сеанс, и выдержали перезапуск браузера и сервера.

+1

Не могли бы вы поделиться соображениями, почему вы отказываетесь от членства в ASP.NET? – Greg

+1

Уверенный Грег, нам нравится простота отношений с базой данных, когда вы сохраняете весь профиль пользователя в простом классе. Также мы используем OpenID, поэтому использование членства в ASP.NET уже было взломом и позволило нескольким идентификаторам OpenID для каждого пользователя практически невозможно. – Pablo

+0

Звучит круто, спасибо. – Greg

ответ

5

Обновление: Первоначальный ответ был предоставлен проектом с использованием MembershipProvider, и это объясняется в самом ответе. Я, спрашивающий, не использую его, поэтому ответ на мою проблему был немного иным, но извлечен из этого ответа. Я поставил свой ответ внизу для тех, кто заботится и оставляет исходную дословную информацию, поскольку он содержит большую ценность.


Да, вы можете использовать FormsAuthentication для своей собственной стратегии. И хотя структура asp.net db вам не подходит, вы можете обеспечить простую реализацию MembershipProvider, чтобы разрешить использование инфраструктуры Membership. Эти две функции не состоят в браке, поэтому вы можете решить, что вам подходит.

Имея в виду ваш вопрос и некоторые комментарии, приведенный пример простого использования модели поставщика, не вступая в брак со стандартными реализациями и схемами db.

Использование форм auth для ваших целей просто. Вам просто нужно предоставить аутентификацию и установить свой собственный билет (cookie).

Использование пользовательского членства почти так же просто. Вы можете реализовать как мало, так и большую часть поставщика, поскольку вам необходимо поддерживать инфраструктурные функции asp.net, которые вы хотели бы использовать.

например. в приведенном ниже примере я показываю, что в процессе входа в систему вы можете просто обработать событие в элементе управления входами, чтобы проверить учетные данные и установить билет. Готово.

Но я также покажу, как использование модели поставщика и внедрение пользовательского поставщика членства может привести к более сильному, более чистому коду. В то время как мы находимся в поставщике пользовательских членских прав, я реализую минимум, необходимый для поддержки использования подсистемы Membership, чтобы обеспечить легкий доступ к метаданным пользователя без необходимости писать собственную инфраструктуру.

Просто отбросьте эти файлы в пустой проект.

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> 

site1.Мастер


<%@ 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 
    } 
} 

Решение на самом деле использовали (в качестве проекта ASP.NET MVC с использованием OpenID):

У меня есть 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

+0

@J. Пабло Фернандес, конечно, на самом деле вы _can_ используете подсистему аутентификации форм как есть с вашей собственной реализацией членства. И если вы хотите подорвать подсистему членства, это просто. Сейчас я работаю над образцом. –

+0

Спасибо Sky Sanders, много! – Pablo

1

Файл cookie ASPXAUTH полностью отделяет токен аутентификации от сеанса, это одна из причин его использования. Если ваша система использует сеанс, то это действительно противоречит тому, что пытается сделать аутентификация форм.

Если все в сеансе, вам действительно не нужен билет на аутентификацию, предполагая, что сеанс защищен.

+0

Я больше не использую проверку подлинности. Мне просто интересно, смогу ли я сохранить состояние входа в cookie ASPXAUTH вместо сеанса. – Pablo

+0

Зарегистрированное состояние в основном контролируется присутствием файла cookie. Если его нет, он не войдет в систему. Использование токена auth вместо обычного cookie не дает вам больше. Даже тогда, если все будет в сеансе, когда вы укажете, то, конечно, сеанс будет отслеживать вход или выход из системы? – blowdart

+0

Независимо от того, вошел ли пользователь в систему, * не * контролируется только наличием файла cookie, иначе любой будет просто создавать cookie и войти в систему. .ASPXAUTH, по-видимому, хранит данные в файле cookie в зашифрованном виде и длится до тех пор, пока я хочу. Session хранит идентификатор в файле cookie и данные в процессе, он обычно длится 20 минут и умирает, если процесс умирает. Я хочу поведение ASPXAUTH для моих данных состояния входа. – Pablo