2013-08-24 58 views
0

请告诉我用于验证网络用户和存储他们的详细资料,我有一类的最佳方式:asp.net认证的WebForms

我应该使用会话或窗体身份验证cookie的?

如何从两种方式访问​​它,比如userclass.username?

我想存储相当多的用户信息来停止数据库调用,例如用户类型,用户全名,地址,邮政编码,foo1,foo2,foo3,foo4等等。我知道这可能会在会话或身份验证cookie用户数据。这个问题与https://stackoverflow.com/questions/18393122/whats-the-best-way-to-authenticate-a-user-and-store-user-details-sessions-or-fo相关联,并且我没有任何帮助

真的可以做一些帮助和建议,因为我有一些系统需要这样做。任何意见赞赏。

感谢

************************************链接**** *************************

基于大致在我的代码:

http://www.shawnmclean.com/blog/2012/01/storing-strongly-typed-object-user-profile-data-in-asp-net-forms-authentication-cookie/

http://www.danharman.net/2011/07/07/storing-custom-data-in-forms-authentication-tickets/

* *******************************编辑************** ***************

定制身份模块

Public Module IdentityExtensions 
Sub New() 
End Sub 

Private _CustomIdentityUser As CustomIdentityUser 

<System.Runtime.CompilerServices.Extension> _ 
Public Function CustomIdentity(identity As System.Security.Principal.IIdentity) As CustomIdentityUser 
    'If _CustomIdentityUser Is Nothing Then 
    '_CustomIdentityUser = DirectCast(identity, CustomIdentityUser) 
    _CustomIdentityUser = Nothing 
    If identity.GetType = GetType(FormsIdentity) Then 
     _CustomIdentityUser = New CustomIdentityUser(DirectCast(identity, FormsIdentity).Ticket) 
    Else 
     If identity.IsAuthenticated Then 
      FormsAuthentication.RedirectToLoginPage() 
     End If 
    End If 

    Return _CustomIdentityUser 
End Function 
End Module 

我的自定义用户身份

Public Class CustomIdentityUser 
Implements System.Security.Principal.IIdentity 

Private ticket As System.Web.Security.FormsAuthenticationTicket 
Private _Auth As Auth 

Public Sub New(ticket As System.Web.Security.FormsAuthenticationTicket) 
    Me.ticket = ticket 
    _Auth = New projectabc.Auth(Me.ticket) 
End Sub 

Public ReadOnly Property Auth As Auth 
    Get 
     Return Me._Auth 
    End Get 
End Property 
Public ReadOnly Property Username As String 
    Get 
     Return Auth.Username 
    End Get 
End Property 

Public ReadOnly Property UserType As Enumerations.EnumUserType 
    Get 
     Return Auth.UserType 
    End Get 
End Property 
Public ReadOnly Property OwnerType As Enumerations.EnumOwnerType 
    Get 
     Return Auth.OwnerType 
    End Get 
End Property 


Public ReadOnly Property AuthenticationType As String Implements System.Security.Principal.IIdentity.AuthenticationType 
    Get 
     Return "Custom" 
    End Get 
End Property 

Public ReadOnly Property IsAuthenticated As Boolean Implements System.Security.Principal.IIdentity.IsAuthenticated 
    Get 
     Return ticket IsNot Nothing 
    End Get 
End Property 

Public ReadOnly Property Name As String Implements System.Security.Principal.IIdentity.Name 
    Get 
     Return Username 
    End Get 
End Property 
End Class 

然后,你可以看到在用户类调用一个auth类主要有该用户的所有属性,并得到和设置等等

Public Class Auth 
Inherits BaseUser 

Public Property _ticket As Web.Security.FormsAuthenticationTicket 
Public RememberMe As Boolean 

Private _IssueDate As DateTime? 
Public ReadOnly Property IssueDate As DateTime? 
    Get 
     Return _IssueDate 
    End Get 
End Property 
Private _Expired As Boolean 
Public ReadOnly Property Expired As Boolean 
    Get 
     Return _Expired 
    End Get 
End Property 
Private _Expiration As DateTime? 
Public ReadOnly Property Expiration As DateTime? 
    Get 
     Return _Expiration 
    End Get 
End Property 

Public Sub New(ticket As System.Web.Security.FormsAuthenticationTicket) 
    Me._ticket = ticket 
    Dim SignOutUser As Boolean = False 
    Try 
     If Not GetUserDetails() Then 
      SignOutUser = True 
     End If 
    Catch ex As Exception 
     SignOutUser = True 
    End Try 
    If SignOutUser Then 
     HttpContext.Current.Response.Redirect("~/", True) 
     SignOut() 
    End If 
End Sub 

Public ReadOnly Property IsAuthenticated() As Boolean 
    Get 
     Return HttpContext.Current.User.Identity.IsAuthenticated 
    End Get 
End Property 

Public Function SetAuthCookie() As Int16 
    Dim encTicket As String 
    Dim userData As String = CreateUserDataString() 
    If userData.Length > 0 And userData.Length < 4000 Then 
     Dim cookiex As HttpCookie = FormsAuthentication.GetAuthCookie(MyBase.Username, True) 
     Dim ticketx As FormsAuthenticationTicket = FormsAuthentication.Decrypt(cookiex.Value) 

     'Dim newTicket = New FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData, ticket.CookiePath) 
     'encTicket = FormsAuthentication.Encrypt(newTicket) 

     'Use existing cookie. Could create new one but would have to copy settings over... 
     'cookie.Value = encTicket 
     'cookie.Expires = newTicket.Expiration.AddHours(24) 

     'HttpContext.Current.Response.Cookies.Add(cookie) 
     Dim ticket As New FormsAuthenticationTicket(1, ticketx.Name, DateTime.Now, ticketx.Expiration, False, userData, ticketx.CookiePath) 
     encTicket = FormsAuthentication.Encrypt(ticket) 
     cookiex.Value = encTicket 
     'Dim cookie As New HttpCookie(FormsAuthentication.FormsCookieName, encTicket) 

     HttpContext.Current.Response.Cookies.Add(cookiex) 
    Else 
     Throw New ArgumentOutOfRangeException("User data length exceeds maximum", New ArgumentOutOfRangeException) 
    End If 

    Return encTicket.Length 
End Function 

Public Function GetUserDetails() As Boolean 
    Dim valid As Boolean = False  

    If _ticket IsNot Nothing Then 
     With _ticket 
      RememberMe = .IsPersistent 
      Username = .Name 
      _IssueDate = .IssueDate 
      _Expired = .Expired 
      _Expiration = .Expiration 

      Try 
       If .UserData.Length > 0 Then 
        valid = SetUserDataFromString(.UserData) 
       Else 
        'we have a problem 
        Return False 
       End If 
      Catch ex As Exception 
       'sign them out as they may have a cookie but the code may have changed so it errors thus make them login again. 
       'SignOut() 
       Throw ex 

      End Try 
     End With 
    End If 

    Return valid 

End Function 

Private Function CreateUserDataString() As String 
    Dim sData As New System.Text.StringBuilder 

    With sData 
     .Append(MyBase.UserID) 
     .Append("|") 'delimeter we are using 
     .Append(Int16.Parse(MyBase.UserType)) 
     .Append("|") 
     .Append(Int16.Parse(MyBase.Security)) 
     .Append("|") 'delimeter we are using 
     .Append(MyBase.FirstName) 
     .Append("|") 
     .Append(MyBase.LastName) 
    .Append("|") 
     .Append(MyBase.foo1) 
    .Append("|") 
     .Append(MyBase.foo2) 
     .Append("|")    
     .Append(MyBase.foo3) 
    .Append("|") 
     .Append(MyBase.foo4) 
    End With 


    Return sData.ToString 
End Function  

    Public Function SetUserDataFromString(userData As String) As Boolean 
    Dim valid As Boolean = False 
    Dim sData As New System.Text.StringBuilder 
    'check we have a delimeter 
    Dim arUserData As String() = userData.Split("|") 
    Try 


    If arUserData.Count >= 9 Then '9 because that the user only stuff 
     With arUserData 
      MyBase.UserID = arUserData(0) 
      MyBase.UserType = arUserData(1) 
      MyBase.Security = arUserData(2) 
      MyBase.FirstName = arUserData(3) 
      MyBase.LastName = arUserData(4) 
      MyBase.foo1 = arUserData(5) 
    MyBase.foo2 = arUserData(6) 
    MyBase.foo3 = arUserData(7) 
    MyBase.foo4 = arUserData(8) 
     End With 
     valid = True 
    Else 
     valid = False 
     End If 

    Catch ex As Exception 
     Throw New ArgumentOutOfRangeException("User data length to short", New ArgumentOutOfRangeException) 
    End Try 
    Return valid 
End Function 

Public Sub SignOut() 
    FormsAuthentication.SignOut() 
End Sub 
+0

用户登录我已经实施了类似的在使用自定义标识的两个项目中进行身份验证(使用表单身份验证票证),并且已经证明是更好的选择来存储您打算反复使用每个请求的用户数据。在你的其他文章中,我不明白你为什么不喜欢为每个请求实例化一个自定义标识!你可以解释吗?你认为可能有多重实例化客户端身份? – Nilesh

+0

感谢您的回复。在我看来,我必须为每个请求对类实例化​​的用户数据的任何自定义属性做出错误。因此,例如userdata.firstname调用和userdata.lastname两次创建类。你的是否这样做。 – Jonnymaboy

+0

号你需要解密用户数据,然后将所有单个对象initialization.Can您对帖子进行编辑和添加您如何实例化自定义主代码中的属性? – Nilesh

回答

1

正如你已经发布的代码可能会得到一些很好的答案。我会尽力按照我的理解来回答,希望有所帮助。 如果没有意义,我已经很久没有和VB保持联系了。

  1. 从哪里调用CustomIdentity?在Else部分的第一种方法中,我认为如果您希望用户重定向到登录页面,您可能需要使用Not IsAuthenticated。大多数情况下,如果票据无效,您甚至不必执行此操作,该框架可为您提供帮助。
  2. CustomIdentityUser你有一个私人成员_Auth,你从来没有使用,而通过属性返回一些值。您正在使用Auth.UserName而不是_Auth.UserName,我不确定这是如何工作,除非用户名是静态成员。
  3. 你为什么要定制依赖于Auth的标识?您可以通过构造器或公开setters将所需数据传递给自定义标识。为什么你需要身份认证中的身份验证票?
  4. Auth类具有到期日期和其他东西是不需要的。您可以使用简单的User类来存储用户的基本信息。为什么你从Auth的构造函数重定向用户?
  5. 我不知道GetUserDetailsSetUserDataFromString为什么你需要所有这些方法。它只是SerializingDeserializing User类的问题。

我知道您必须在那里引用一些博客来实现此身份验证,但您有很多范围来简化此操作。

阅读this后。具体说明自定义主体如何实施以及如何设置authticket以及如何设置方法。

下面是一些示例代码,这可能有助于

interface ICustomPrincipal : IPrincipal 
{ 
    int UserId { get; set; } 
    string FirstName { get; set; } 
    string LastName { get; set; } 
} 

public class CustomPrincipal : ICustomPrincipal 
{ 
    public CustomPrincipal() 
    { 

    } 

    public CustomPrincipal(string userName) 
    { 
     Identity = new GenericIdentity(userName); 
    } 

    public int UserId 
    { 
     get; 
     set; 
    } 

    public string FirstName 
    { 
     get; 
     set; 
    } 

    public string LastName 
    { 
     get; 
     set; 
    } 

    public IIdentity Identity 
    { 
     get; 
     private set; 
    } 

    public bool IsInRole(string role) 
    { 
     return false; 
    } 

} 

public class User 
{ 
    public string UserName { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Email { get; set; } 
} 

public static class FormsAuthHelper 
{ 
    public static void SetAuthTicket(User user, HttpContextBase context) 
    { 
     var serializer = new JavaScriptSerializer(); 
     var userData = serializer.Serialize(user); 
     var authTicket = new FormsAuthenticationTicket(
      1, user.UserName, 
      DateTime.Now, DateTime.Now.AddMinutes(30), 
      false, userData); 
     var ticket = FormsAuthentication.Encrypt(authTicket); 
     var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticket); 
     context.Response.Cookies.Add(faCookie); 
    } 

    public static void Logout() 
    { 
     FormsAuthentication.SignOut(); 
     FormsAuthentication.RedirectToLoginPage(); 
    } 

    public static CustomPrincipal GetPrincipal(User user) 
    { 
     return new CustomPrincipal(user.UserName) { FirstName = user.FirstName, LastName = user.LastName, UserId = user.EntityId }; 
    } 
} 

发布身份验证请求事件看起来是这样的

protected void Application_PostAuthenticateRequest(object sender, EventArgs e) 
{ 
    var authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
    if (authCookie == null || authCookie.Value == string.Empty) 
     return; 

    try 
    { 
     var ticket = FormsAuthentication.Decrypt(authCookie.Value); 
     var serializer = new JavaScriptSerializer(); 
     var user = serializer.Deserialize<User>(ticket.UserData); 
     var newUser = FormsAuthHelper.GetPrincipal(user); 

     HttpContext.Current.User = newUser; 
    } 
    catch 
    { 
      //do nothing 
    } 
} 

最后当

public ActionResult Login(LoginModel loginModel) 
{ 
    if (ModelState.IsValid) 
    { 
     var user = _userRepository.Get(x => x.UserName == loginModel.UserName).SingleOrDefault(); 
     if (user != null && PasswordHash.ValidatePassword(loginModel.Password, user.Password)) 
     { 
     FormsAuthHelper.SetAuthTicket(user, HttpContext); 
     return RedirectToAction("Index", "Home"); 
     } 
     ModelState.AddModelError("NotFound", "User not found"); 
    } 
    return View(loginModel); 
} 
+0

非常感谢你这个伟大的答案。为了回答您的一些点: – Jonnymaboy

+0

1.我不真正调用调用,这是所谓的customidentity性质的访问延期。 2,私人成员_auth被设置为sub new,然后我有一个公共属性auth。所以auth只是返回_auth。 3:你的权利根本不需要。 4:基本上我在这里重定向,如果有任何问题获取用户的详细信息和分配属性,即如果一个cookie没有过期,我改变了我需要用户重新登录的属性顺序。 5:我有额外的属性取决于用户类型。我会根据你的代码做一个测试应用程序。链接到博客添加到问题。谢谢。 – Jonnymaboy

+0

该帖子有很多代码,我可能忽略了一些属性。 :)。希望我发布的代码可以帮助你,或者至少让你有机会探索更多。 – Nilesh