ASP.NET:我在哪里可以找到快速自定义成员资格提供程序

时间:2010-04-10 21:02:06

标签: c# asp.net membership-provider

是否有人已经在线创建的自定义asp.net成员资格提供程序。我想使用自定义提供程序,因为他们的角色提供程序非常缓慢,我想使用整数而不是guids来获取用户ID

1 个答案:

答案 0 :(得分:4)

标准成员资格提供程序API没有替代实现,因为它基于非通用接口。

但是,在实践中,整个成员资格API几乎没有增加价值,并且很容易推出自己的解决方案。

为了帮助您入门,我通常会使用一些辅助类。

首先,一些处理cookie和Forms身份验证的代码:

public interface ISecurityPersistenceProvider
{
    void SetAuthCookie( int version, string name, DateTime expireDate, bool persistent, string cookieData );
    HttpCookie GetAuthCookie();
    string GetAuthCookieValue( out string name );
    void RemoveAuthCookie();
}

public class FormsPersistenceProvider : ISecurityPersistenceProvider
{
    public void SetAuthCookie( int version, string name, DateTime expireDate, bool persistent, string cookieData )
    {
        var ticket = new FormsAuthenticationTicket( version, name, DateTime.Now, expireDate, persistent, cookieData );
        string secureTicket = FormsAuthentication.Encrypt( ticket );
        var cookie = new HttpCookie( FormsAuthentication.FormsCookieName, secureTicket );
        cookie.Expires = ticket.Expiration;
        HttpContext.Current.Response.Cookies.Add( cookie );
    }

    public HttpCookie GetAuthCookie()
    {
        HttpCookie cookie = HttpContext.Current.Request.Cookies[ FormsAuthentication.FormsCookieName ];
        return cookie;
    }

    public string GetAuthCookieValue( out string name )
    {
        HttpCookie cookie = GetAuthCookie();
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt( cookie.Value );
        name = ticket.Name;
        return ticket.UserData;
    }

    public void RemoveAuthCookie()
    {
        FormsAuthentication.SignOut();
    }
}

其次,一些代码用于针对数据存储验证用户。您需要为此界面提供自己的实现(和/或根据您的需要进行调整)。

public interface ISecurityUserProvider<T> where T : class
{
    T AuthenticateUser( string customer, string userName, string passwordHash );
    T GetUser( string customer, string userName );
    string[] GetRoles( T user );
}

第三,让我们向调用者返回附加信息的类(这将由下面的SecurityManager类使用):

public class AuthenticationResult
{
    #region Fields/Properties
    public bool Success { get; internal set; }
    public string Message { get; internal set; }
    public IDictionary<string,string> Errors { get; internal set; }
    #endregion

    #region Constructors
    public AuthenticationResult( bool success, string message ) : this( success, message, null )
    {
    }
    public AuthenticationResult( bool success, string message, IDictionary<string, string> errors )
    {
        Success = success;
        Message = message;
        Errors = errors ?? new Dictionary<string, string>();
    }
    #endregion
}

我还使用了一个自定义主体类,它允许我将自定义对象与基本的IPrincipal实现相关联。这只是一个快速摘录,可以提供您的想法:

public class CodeworksPrincipal<TUserData> : IPrincipal where TUserData : class
{
    private string name;
    private string[] roles;
    private string allRoles;
    private TUserData userData;

    public CodeworksPrincipal( string name, string[] roles, TUserData userData )
    {
        // init fields, etc.
    }
}

最后,SecurityManager类具有大量逻辑:

public class SecurityManager<T,TPersistenceProvider,TUserProvider> 
    where T : class
    where TPersistenceProvider : ISecurityPersistenceProvider
    where TUserProvider : ISecurityUserProvider<T>
{
    private readonly ISecurityPersistenceProvider persistenceProvider;
    private readonly ISecurityUserProvider<T> userProvider;
    // NOTE this constant is used to validate the validity of the cookie (see below)
    private const int CookieParameterCount = 3;

    public SecurityManager( ISecurityPersistenceProvider persistenceProvider, ISecurityUserProvider<T> userProvider )
    {
         this.persistenceProvider = persistenceProvider;
         this.userProvider = userProvider;
    }

    #region Properties
    protected ISecurityPersistenceProvider PersistenceProvider
    {
        get { return persistenceProvider; }
    }
    protected ISecurityUserProvider<T> UserProvider
    {
        get { return userProvider; }
    }
    public IIdentity CurrentIdentity
    {
        get { return Thread.CurrentPrincipal.Identity; }
    }
    public bool IsAuthenticated
    {
        get
        {
            IPrincipal principal = Thread.CurrentPrincipal;
            return principal != null && principal.Identity != null && principal.Identity.IsAuthenticated;
        }
    }
    public bool IsInRole( string roleName )
    {
        IPrincipal principal = Thread.CurrentPrincipal;
        return IsAuthenticated && principal.IsInRole( roleName );
    }
    public string UserName
    {
        get { return IsAuthenticated ? CurrentIdentity.Name : ""; }
    }
    #endregion

    #region Authentication
    public AuthenticationResult Authenticate( string userName, string password, bool persistent, string visitorAddress )
    {
        return Authenticate( null, userName, password, persistent, visitorAddress );
    }
    public AuthenticationResult Authenticate( string customer, string userName, string password, bool persistent, string visitorAddress )
    {
        AuthenticationResult result = ValidateInput( userName, password );
        if( ! result.Success )
            return result;
        string passwordHash = GetCryptographicHash( password );
        T user = userProvider.AuthenticateUser( customer, userName, passwordHash );
        if( user == null )
            return new AuthenticationResult( false, "Unable to login using the specified credentials.", null );
        if( ! IsAuthorizedVisitor( user, visitorAddress ) )
            return new AuthenticationResult( false, "Credentials do not allow login from your current IP address.", null );
        CurrentPrincipal = new CodeworksPrincipal<T>( userName, userProvider.GetRoles( user ), user );
        // remember to change CookieParameterCount if you change parameter count here
        string cookieData = String.Format( "{0}|{1}|{2}", CurrentIdentity.Name, customer, CurrentPrincipal.AllRoles );
        persistenceProvider.SetAuthCookie( 1, userName, DateTime.Now.AddMonths( 1 ), persistent, cookieData );
        // TODO create an audit log entry for the current request
        return new AuthenticationResult( true, null, null );
    }
    private AuthenticationResult ValidateInput( string login, string password )
    {
        var result = new AuthenticationResult( true, "Invalid or missing credentials." );
        if( String.IsNullOrEmpty( login ) )
        {
            result.Success = false;
            result.Errors.Add( "login", "You must specify an alias or email address." );
        }
        if( String.IsNullOrEmpty( password ) )
        {
            result.Success = false;
            result.Errors.Add( "password", "You must specify a password." );
        }
        // TODO add message to inform users of other requirements.
        return result;            
    }
    #endregion

    #region Cookie Authentication
    public void CookieAuthenticate( string visitorAddress )
    {
        HttpCookie cookie = persistenceProvider.GetAuthCookie();
        if( cookie != null )
        {
            string userName;
            string userData = persistenceProvider.GetAuthCookieValue( out userName );
            string[] cookieData = userData.Split( '|' );
            // extract data from cookie
            bool isValid = cookieData.Length == CookieParameterCount && 
                           ! string.IsNullOrEmpty( cookieData[ 0 ] ) &&
                           cookieData[ 0 ] == userName &&
                           IsAuthorizedVisitor( cookieData[ 1 ], cookieData[ 0 ], visitorAddress );
            if( isValid )
            {
                string customer = cookieData[ 1 ];
                string[] roles = cookieData[ 2 ].Split( ',' );
                T user = userProvider.GetUser( customer, userName );

                CurrentPrincipal = new CodeworksPrincipal<T>( userName, roles, user );
            }
        }
    }
    #endregion

    #region Logout
    public void Logout()
    {
        // logout the current user
        if( HttpContext.Current.Request.IsAuthenticated || CurrentPrincipal != null )
        {
            long address = GetAddressFromString( HttpContext.Request.UserHostAddress );
            // TODO add audit log entry
            // sign out current user
            persistenceProvider.RemoveAuthCookie();
            CurrentPrincipal = null;
        }
    }
    #endregion

    #region VisitorAddress Checks
    public long GetAddressFromString( string address )
    {
        IPAddress ipAddress;
        if( IPAddress.TryParse( address, out ipAddress ) )
        {
            byte[] segments = ipAddress.GetAddressBytes();
            long result = 0;
            for( int i = 0; i < segments.Length; i++ )
            {
                result += segments[ i ] << (i * 8);
            }
            return result;
        }
        return long.MaxValue;
    }

    public bool IsAuthorizedVisitor( string customer, string userName, string visitorAddress )
    {
        return IsAuthorizedVisitor( customer, userName, GetAddressFromString( visitorAddress ) );
    }

    public bool IsAuthorizedVisitor( string customer, string userName, long visitorAddress )
    {
        T user = userProvider.GetUser( customer, userName );
        return IsAuthorizedVisitor( user, visitorAddress );
    }

    public bool IsAuthorizedVisitor( T user, string visitorAddress )
    {
        return IsAuthorizedVisitor( user, GetAddressFromString( visitorAddress ) );
    }

    public bool IsAuthorizedVisitor( T user, long visitorAddress )
    {
        if( user == null || visitorAddress == 0 )
            return false;
        if( user.Hosts.Count == 0 )
            return true;
        foreach( Host host in user.Hosts )
        {
            if( IsAuthorizedVisitor( host.HostAddress, host.HostMask, visitorAddress ) )
                return true;
        }
        return true;
    }

    public bool IsAuthorizedVisitor( long allowedAddress, long allowedMask, long visitorAddress )
    {
        long requireMask = allowedAddress & allowedMask;
        return (visitorAddress & requireMask) == requireMask;
    }
    #endregion

    #region Cryptographic Helpers
    public static string GetCryptographicHash( string password )
    {
        SHA256 hash = SHA256.Create();
        byte[] input = Encoding.UTF8.GetBytes( password );
        byte[] output = hash.ComputeHash( input );
        return Convert.ToBase64String( output );
    }
    #endregion
}

如果您不需要,可以删除位以检查访问者主机地址。

我希望这有助于即使它本身不是会员提供者; - )