一步一步实现FormsAuthentic验证登录

时间:2022-07-31 14:35:50

  本文不讲原理,只讲用法,原理性的东西网上特别多,不过还是会对一些要用到的东西进行解释,不深入讲原理。本文中用的是Vs2012   .net mvc 4.0。原理看这篇文章,看完这个文章绝对受益匪浅。

说下登录的整个流程:用户输入账号密码->点击提交->数据提交到后台控制器->去数据库取得用户资料->如果登录成功->将数据写入cookie(也就是写入forms身份验证)->返回给控制器登录状态->对相应的登录状态进行处理。

第一步:新建一个.net mvc 4.0的解决方案,然后配置 WebConfig文件

在 <system.web>节点下添加以下代码:

    <authentication mode="Forms" name="CookieName">
<forms loginUrl="~/Login/Login" timeout="" />
</authentication>

简要说明 一下上面的代码:

  loginUrl登录页的URL。通过FormsAuthentication.LoginUrl属性可以得到该配置值。当调用FormsAuthentication.RedirectToLoginPage()方法时,客户端请求将被重定向到该属性所指定的页面。如果没有设置这个属性,.net也会尝试在目录下寻找名为login.aspx的文件;

  mode属性,就是选择的是Forms模式的登录,还有其他模式(Windows,Passport等等);

  name属性,是cookie的名称,在获取登录的cookie时需要通过这个name来取得登陆用的Cookie;

  timeout属性,就是Cookie的过期时间,可以同slidingExpiration属性 配合使用;

  slidingExpiration属性,是否启用“弹性过期时间”,如果该属性设置为false,从首次验证之后过timeout时间后Cookie即过期;如果该属性为true,则从上次请求该开始过timeout时间才过期,也就是说,如果在timeout时间内,再次向服务器端发送请求,则Cookie将永远不会过期。

以上是主要用到的一些属性。

关于配置文件,若有数据库,当然还要配置数据库连接,这里就不多说了。

第二步:创建用户类型:UserModel

  这个类主要是用来保存登录的用户的对象。

    /// <summary>使用者</summary>
public class UserModels
{
/// <summary>使用者编号</summary>
[Display(Name = "使用者编号")]
public int users_db_id { get; set; } /// <summary>用戶姓名</summary>
[Display(Name = "用户姓名")]
public string users_name { get; set; } /// <summary>账号</summary>
[Display(Name = "账号")]
public string login_id { get; set; } /// <summary>密码</summary>
[Display(Name = "密码")]
public string login_pwd { get; set; } /// <summary>身分</summary>
[Display(Name = "身份")]
public int users_position { get; set; }
}

在这里不对角色做太多的处理,users_position简要的表明用户所属的角色。

第三步:添加一个生产身份验证的类SetCookies

public class SetCookies
{
public static void SetCookie(string id, bool chkAutoLogin, int role)
{
DateTime EndDate = DateTime.Now;
if (chkAutoLogin)
{
EndDate = DateTime.Now.AddDays();
} string Role = string.Empty; switch (role)//这里简要这几个角色,实际应用中可以从数据库中读取角色
{
case :
Role = "Agent";//客户
break;
case :
Role = "Business";//供应商
break;
case :
Role = "Financial";//财务
break;
case :
Role = "Boss";//老板
break;
} //生产身份验证
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(,id,DateTime.Now,EndDate,true,Role,FormsAuthentication.FormsCookiePath); //对身份验证的标识进行加密
string encTicket = FormsAuthentication.Encrypt(ticket); //创建将要写入到客户端的Cookie
HttpCookie newCookie = new HttpCookie(FormsAuthentication.FormsCookieName, id);
       //如果勾选了是否自动登录,则将过期时间推迟14天
if (chkAutoLogin)
{
newCookie.Expires = EndDate;
}
//写入到客户端
HttpContext.Current.Request.Cookies.Add(newCookie);
}
}
FormsAuthentication.FormsCookieName 就是刚才在配置文件中的name属性的值
这里小写的role 类似于数据库用户表的外键(一般都有一个权限表,这里假设只有用户表和角色表),大写的Role类似于数据库角色表的角色名称,这里简要处理角色,所以在真正项目中,可以在这里从数据库读取用户的角色,然后将其角色用逗号(或者其他符号隔开),生产一个角色字符串(类似于Role="角色A,角色B,角色C")。最后将这个Role传到FormsAuthenticationTicket 类的UserData参数中,在后面判断角色的时候用到,UserData这个参数也不一定要放角色,也可以放其他要用到的数据,因项目而异。对FormsAuthenticationTicket 这个类不清楚的,可以自己看看定义(最好还是去看看,对理解这个Forms验证登录有帮助)。以上这个类也算是Forms登录中最重要的一步了。
第三步:验证登录,从数据库拿数据,然后返回登录状态
先给出登录状态的类,这是一个枚举类型
    public class EnumList
{
public enum LoginSts
{
/// <summary>登入成功</summary>
Sucess,
/// <summary>密碼錯誤</summary>
PasswordError,
/// <summary>帳號不存在</summary>
NoExists,
/// <summary>登入失敗</summary>
LoginError
}
}

这个是枚举类型,不做解释。

 public class SetLoginRepository
{
public static Tuple<EnumList.LoginSts, int> SetLogin(string id, string pwd = "", bool IsAutoLogin = false)
{
       //关于以下这种类型的用法,请看另一篇文章,或者网上搜索Tuple,没有这个类型对本文章没有多大的影响,因为这个类型就是用来返回 多种数据类型 的数据 的类型
Tuple<EnumList.LoginSts, int> status = new Tuple<EnumList.LoginSts, int>(EnumList.LoginSts.LoginError, );
UserModels userData = GetUserData(id);//取得用户数据
if (userData != null)
{
if (string.IsNullOrWhiteSpace(pwd) || userData.login_pwd == pwd)
{
//你可以在这里将登录的用户对象存放到Session中,以便将来要用到这个对象,比如Session["Account"]=userData
SetCookies.SetCookie(id, IsAutoLogin, userData.users_position);//这里就是调用上面的那个写入身份验证的方法
status = new Tuple<EnumList.LoginSts, int>(EnumList.LoginSts.Sucess, userData.users_position);
}
else
{
status = new Tuple<EnumList.LoginSts, int>(EnumList.LoginSts.PasswordError, );
}
}
else
{
status = new Tuple<EnumList.LoginSts, int>(EnumList.LoginSts.NoExists, );
}
return status;
}
    //从数据库取得用户数据,这里使用的是Ado.net
public static UserModels GetUserData(string id)
{
UserModels model = new UserModels();
string connStr = ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "select top 1 * from users_db where login_id='" + id + "' and datastatus=1 "; SqlDataReader sdr = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow);
if (sdr.Read())
{
model.users_db_id = Convert.ToInt32(sdr["users_db_id"]);
model.login_id = sdr["login_id"].ToString();
model.users_name = sdr["users_name"].ToString();
model.login_pwd = sdr["login_pwd"].ToString();
model.datastatus = Convert.ToBoolean(sdr["datastatus"].ToString());
model.users_position = Convert.ToInt32(sdr["users_position"]);
}
}
return model;
}
}

关于Tuple<T1,T2...T8> 的用法请看另一篇文章.Net 4.0特性 Tuple元组

。这里先简要解释一下它的作用,Tuple就是可以自定义任何类型,在返回值的时候,可以返回任意多个类型的数据,我认为它的作用就是能返回多种类型的数据。然后可以通过该类型的实例获得相应的值,获取值的方法是比如这个类的实例叫tuple,则取得值的方法是tuple.Item1,tuple.Item2....tuple.Item8,通过这个可以取得对应位置的值。这就很方便的给我们提供了可以在一个方法里面返回多种数据类型。

第四步:也是非常重要的一步,Global文件添加一个方法:

        //取得自定义的角色
public void Application_AuthenticateRequest(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
// 先取得该使用者的 FormsIdentity
FormsIdentity id = (FormsIdentity)User.Identity;
// 再取出使用者的 FormsAuthenticationTicket
FormsAuthenticationTicket ticket = id.Ticket;
// 將储存在 FormsAuthenticationTicket 中的角色定义取出,并转成字符串数组
string[] roles = ticket.UserData.Split(new char[] { ',' });
// 指派角色到目前这个 HttpContext 的 User 物件去
Context.User = new GenericPrincipal(Context.User.Identity, roles);
}
}

这个方法是在Global文件中定义的,在客户端每次发一个请求都会先经过这个方法,即使是Ajax请求也同样要先经过这个方法。注释写得很清楚,不多说。

第五步 :写控制器LoginController

        public ActionResult SetLogin(string Name, string Password)
{
if (ModelState.IsValid)
{
Tuple<EnumList.LoginSts, int> status = SetLoginRepository.SetLogin(Name, Password, true);
switch (status.Item1)//这个.Item1就是Tuple类型取得对应位置类型 的值 的方法
{
case EnumList.LoginSts.Sucess:
if (status.Item2 == )
{
//对登录成功的情况进行处理,可以跳转到列表页或者网站首页之类的
return RedirectToAction("actionName","ControlName");
}
break;
case EnumList.LoginSts.NoExists:
//对用户不存在的情况进行处理
break;
case EnumList.LoginSts.PasswordError:
//对密码错误的情况进行处理
break;
}
}
return View();
}

顺便附上退出的代码:

        public static void ClearSessionAndCookie()
{
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName);
cookie.Expires.AddDays(-);
HttpContext.Current.Response.Cookies.Add(cookie);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Session.Abandon();
FormsAuthentication.SignOut();
}

代码很简单,就是清除Cookie,清除Cookie的方法就是将它的过期时间设置为前一天。然后FormsAuthentication.SignOut()就退出了。

整个流程完成,前台页面的代码就不写了,就两个文本框加上一个登录按钮。还有一个是否自动登录的按钮,这里就不演示了。