前一段时间博客园新闻里看到的雄文:你会做Web上的用户登录功能吗?
弱弱地表示,按照文章中的标准,写了这么长时间的程序,还真的不完全会写web上的用户登录功能,或者说不能完全按照这个标准写出复杂的登录功能来。也许是被微软惯坏了,深刻反省。
好了,言归正传,下面就来重点说说本文要讨论的:实现基于Forms认证的注册和登录等用户基础服务。
众所周知,asp.net提供了“Forms”、“Windows”、“Passport”和“None”四种验证模式。在web.config文件中,经常看到类似这样的一段配置节:
<authentication mode="Forms">
<forms cookieless="UseCookies" defaultUrl="~/Default.aspx" loginUrl="~/Login.aspx" timeout="2880"></forms>
</authentication>
这里的配置文件就和下面要讨论的被普遍使用的Forms认证模式有关。
要使用Forms认证,通常情况下,有了上面的配置,在你的代码中调用几个类如FormsAuthentication等等就可以实现基本的登录注册改密等功能了(如何调用MS的类库实现登录注册等功能不是本文讨论的重点)。
你可能会问MS是怎么实现用户注册登录功能的呢?这里只能简单告诉你,和MembershipProvider有关。如果你确实有心决定知道MS的登录组件是怎么工作的,不妨查看一下它的源码。一致认为微软做的太复杂了,而且不怎么符合实际的业务需求,至少我工作过的公司还没碰到过拿微软实现的那一套直接在项目中使用的。
对照着MembershipProvider的源码,自己简单整理并抽象出以下几个常用接口方法:
IUserService public interface IUserService
{
/// <summary>
/// 创建(注册)用户
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
int CreateUser(UserInfo userInfo);
/// <summary>
/// 激活用户
/// </summary>
/// <param name="userId"></param>
/// <param name="verifyCode"></param>
/// <returns></returns>
int ActiveUser(int userId, string verifyCode);
/// <summary>
/// 登录 (根据用户名和密码获取用户信息)
/// </summary>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <returns></returns>
UserInfo Login(string userName, string password);
/// <summary>
/// 验证用户是否合法
/// </summary>
/// <param name="identity"></param>
/// <returns></returns>
bool VerifyUser(IIdentity identity);
/// <summary>
/// 验证用户是否合法
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
UserInfo VerifyUser(UserInfo userInfo);
/// <summary>
/// 退出
/// </summary>
void Logout();
/// <summary>
/// 获取当前登录用户
/// </summary>
/// <returns></returns>
UserInfo GetCurrentUser();
/// <summary>
/// 根据用户id获取一个用户
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
UserInfo GetUserByUserId(int userId);
/// <summary>
/// 根据用户名获取用户
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
UserInfo GetUserByUserName(string userName);
/// <summary>
/// 根据email获取注册用户信息
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
UserInfo GetUserByEmail(string email);
/// <summary>
/// 修改密码
/// </summary>
/// <param name="userId"></param>
/// <param name="strOldPwd"></param>
/// <param name="strNewPwd"></param>
/// <returns></returns>
int ResetPassword(int userId, string strOldPwd, string strNewPwd);
/// <summary>
/// 更新用户信息
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
int UpdateUser(UserInfo userInfo);
/// <summary>
/// 删除(锁定)用户
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
int DeleteUser(int userId);
}
面向接口编程有多重要当然不需要多做说明。
接着,有接口自然还需要有具体实现。具体的实现无非按照特定的规则进行特定的处理:
UserServicepublic class UserService : IUserService
{
public UserDAO Dao { get; set; }
public UserService()
{
Dao = new UserDAO();
}
public int CreateUser(UserInfo userInfo)
{
return Dao.CreateUser(userInfo);
}
public int ActiveUser(int userId, string verifyCode)
{
return Dao.ActiveUser(userId, verifyCode);
}
public UserInfo Login(string userName, string password)
{
UserInfo userInfo = Dao.GetUser(userName, password);
if (userInfo != null)
{
FormsAuthentication.SetAuthCookie(userInfo.UserName, false);
userInfo.IsAuthenticated = HttpContext.Current.User.Identity.IsAuthenticated;
}
return userInfo;
}
public bool VerifyUser(IIdentity identity)
{
bool flag = false;
UserInfo userInfo = Dao.GetUserByUserName(identity.Name);
if (userInfo != null)
{
UserInfo currentUser = VerifyUser(userInfo);
if (currentUser != null)
{
flag = true;
}
}
return flag;
}
public UserInfo VerifyUser(UserInfo userInfo)
{
//自定义规则验证用户
string verifyCode = UserUtil.GenerateVerifyCode(userInfo.UserId, userInfo.Email, userInfo.CreateDate);
return Dao.VerifyUser(userInfo, verifyCode);
}
public void Logout()
{
FormsAuthentication.SignOut();
}
public UserInfo GetCurrentUser()
{
UserInfo result = null;
if (HttpContext.Current.User != null)
{
result = Dao.GetUserByUserName(HttpContext.Current.User.Identity.Name);
}
return result;
}
public UserInfo GetUserByUserId(int userId)
{
return Dao.GetUserByUserId(userId);
}
public UserInfo GetUserByUserName(string userName)
{
return Dao.GetUserByUserName(userName);
}
public UserInfo GetUserByEmail(string email)
{
return Dao.GetUserByEmail(email);
}
public int ResetPassword(int userId, string strOldPwd, string strNewPwd)
{
return Dao.ResetPassword(userId, strOldPwd, strNewPwd);
}
public int UpdateUser(UserInfo userInfo)
{
return Dao.UpdateUser(userInfo);
}
public int DeleteUser(int userId)
{
return Dao.DeleteUser(userId);
}
}
UserInfo是自定义的用户信息实体类:
[Serializable]
public class UserInfo : IIdentity, IPrincipal
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public int UserType { get; set; }
public string Email { get; set; }
public DateTime CreateDate { get; set; }
public DateTime UpdateDate { get; set; }
public string Name { get; set; }
public string AuthenticationType
{
get { return "Forms"; }
}
public bool IsAuthenticated { get; set; }
public IIdentity Identity
{
get { return this; }
}
public bool IsInRole(string role)
{
return false;
}
}
简单示例,没有列举其他常见属性。
DAO的实现就不贴了,无非访问库做一些CRUD的操作。
这样一个简单实用的用户服务大功告成……所以说做MS的程序员是幸福的,前提是你得熟悉原理思路清晰。
到这里你还会觉得web上的用户登录功能很难吗?不难吧?!
实际上你还可以继承MembershipProvider扩展实现自己的MembershipProvider,同时还可以实现自己的角色权限管理等常用用户相关功能,区区一个简单的登录功能自然不在话下。
当然你完全可以独树一帜,实现自定义的复杂规则下的注册登录安全认证等等功能,正如本文开篇提到的那片雄文所讲的那样。