2
是否有任何定製的asp.net會員供應商已經有人在線創建。我想使用自定義提供程序,因爲它們的角色提供程序非常緩慢,並且我想使用整數而不是用戶標識的GUIDASP.NET:我在哪裏可以找到一個快速定製會員供應商
是否有任何定製的asp.net會員供應商已經有人在線創建。我想使用自定義提供程序,因爲它們的角色提供程序非常緩慢,並且我想使用整數而不是用戶標識的GUIDASP.NET:我在哪裏可以找到一個快速定製會員供應商
標準成員資格提供程序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);
}
第三,一個類來讓我們回到呼叫者的附加信息(這將通過下面的安全管理器類中使用):
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
}
可以剝離出位,檢查訪客主機地址,如果你不需要它。
我希望這可以幫助,即使它本身不是會員供應商;-)
這太棒了。但是我會留下這個問題,因爲我還需要數據庫連接方面的幫助。謝謝 – Luke101 2010-04-12 21:54:57
你應該爲此發佈一個單獨的問題,因爲它不是真正的認證相關。 您只需要實現ISecurityUserProvider接口的數據庫方法。 – 2010-04-13 14:04:51