【问题标题】:ASP.NET: Where can I find a fast custom membership providerASP.NET:在哪里可以找到快速的自定义成员资格提供程序
【发布时间】:2010-04-11 14:29:12
【问题描述】:

是否有人已经在线创建了任何自定义 asp.net 会员提供程序。我想使用自定义提供程序,因为他们的角色提供程序非常缓慢,我想使用整数而不是用户 ID 的 guid

【问题讨论】:

    标签: c# asp.net membership-provider


    【解决方案1】:

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

    然而,在实践中,整个会员 API 几乎没有什么价值,而且很容易推出自己的解决方案。

    为了帮助您从这里开始,我提供了一些我通常使用的帮助类。

    首先,一些处理cookies和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
    }
    

    如果不需要,可以去掉这些位来检查访问者的主机地址。

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

    【讨论】:

    • 这太棒了。但我会留下这个问题,因为我还需要数据库连接方面的帮助。谢谢
    • 您应该为此发布一个单独的问题,因为它与身份验证无关。您只需要实现 ISecurityUserProvider 接口的数据库方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多