Asp.Net MVC 身份验证-Forms

在MVC中对于需要登录才可以访问的页面,只需要在对应的Controller或Action上添加特性[Authorize]就可以限制非登录用户访问该页面。那么如果实现登录?

Form登录

应用程序确认用户状态

HTTP协议是无状态的。所以上一次请求和下一次请求并不能相互关联起来,就是说这些请求并不能确定是哪个用户和用户的状态。但是对于登录来说,我们就需要准确的知道用户的状态及是哪个用户。

通常有两种情况来记录用户状态。

  • 一种在服务端通过Session来标识。

  • 一种通过Cookie在客户端标识用户。(用户每次请求应用程序时都会携带该Cookie。)

Form登录实现

Forms 身份验证将身份验证标记保留在 Cookie 或页的 URL 中。Forms 身份验证通过 FormsAuthenticationModule 类参与到 ASP.NET 页面的生命周期中。可以通过 FormsAuthentication 类访问 Forms 身份验证信息和功能。

步骤一

Web.Config配置文件中指定验证的方式为Form,并设置跳转的登录地址和Cookie的名称,及超时时间等。

<system.web>
<authentication mode="Forms">
<forms loginUrl="/Home/Login" timeout="120" cookieless="UseCookies" name="LoginCookieName"></forms>
</authentication>
</system.web>

设置该配置文件,并不需要特意给Action传递returnUrl,就可以获取到跳转地址。

Form特性的详细说明

此时当未登录的用户访问有[Authorize]特性的Action操作时,会被跳转到Login页面,同时Login页面的URL后面会添加一个加密的ReturnUrl地址,该地址指向之前访问的有[Authorize]特性的Action地址。


步骤二

之前提到Form认证其实就是生成了一个Cookie,存放到用户的浏览器中。通过FormAuthenication.SetAuthCookie(userName,true);设置验证登录的Cookie。再通过页面跳转将Cookie响应给客户端。

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel vm,string returnUrl)
{
if (ModelState.IsValid)
{
//用户名,密码验证 FormsAuthentication.SetAuthCookie(vm.UserName, true); //设置Form验证Cookie,后一个 参数为是否创建持久Cookie。及true为可以在用户浏览器上保存的。false为不在浏览器上保存。
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Detail");
}
else
return View(vm);
}

此时当我们登录以后会在浏览器中生成一个跟配置文件中名称相同的Cookie

如:

该Cookie就是我们已经登录,通过Form验证的凭证。

此时我们就可以访问那些需要登录才能访问的页面。


注销

删除对应的Cookie即可实现注销,代码如下:

[Authorize]
public ActionResult LogOut()
{
FormsAuthentication.SignOut();//通过Forms验证来删除Cookie
return View();
}

角色添加

有些页面可能只允许特定用户才能访问,在MVC中可以通过[Authorize(Roles="VIP")]设置Action或Controller,表示只有角色为VIP的用户才可以访问该页面。

如:只有登录且用户角色为VIP的才可以访问这个页面。

[Authorize(Roles = "VIP")]
public ActionResult VIP()
{
return View();
}

同时

需要在设置Form验证凭据时把用户角色添加到Cookie。

如:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel vm, string returnUrl)
{
if (ModelState.IsValid)
{
//用户名,密码验证 //FormsAuthentication.SetAuthCookie(vm.UserName, true); //设置Form验证Cookie,后一个 参数为是否创建持久Cookie。及true为可以在用户浏览器上保存的。false为不在浏览器上保存。
//if (Url.IsLocalUrl(returnUrl))
//{
// return Redirect(returnUrl);
//}
//return RedirectToAction("Detail"); vm.Role = "VIP";
var authTicket = new FormsAuthenticationTicket(
1, // 版本
vm.UserName, // 用户名称
DateTime.Now, // 创建日期
DateTime.Now.AddMinutes(20), // 过期时间
vm.Remember, // 是否记住
vm.Role // 用户角色
); string encryptedTicket = FormsAuthentication.Encrypt(authTicket); var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
authCookie.HttpOnly = true;//客户端脚本不能访问
authCookie.Secure = FormsAuthentication.RequireSSL;//是否仅用https传递cookie
authCookie.Domain = FormsAuthentication.CookieDomain;//与cookie关联的域
authCookie.Path = FormsAuthentication.FormsCookiePath;//cookie关联的虚拟路径
Response.Cookies.Add(authCookie);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
} return RedirectToAction("Detail");
}
else
return View(vm);
}

在Global.asax.cs文件中重写Application_AuthenticateRequest事件

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
//获取Cookie
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == "")
return; FormsAuthenticationTicket authTicket;
try
{
//解析Cookie
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch
{
return;
} // 解析权限
string[] roles = authTicket.UserData.Split(';'); if (Context.User != null)
//把权限赋值给当前用户
Context.User = new GenericPrincipal(Context.User.Identity, roles);
}

至此最简单的Form身份验证实现了。但是该Cookie只包含了用户名,没有其他信息。如果要包含其他信息,可以通过扩展用户的身份标识(HttpContext.User实例)来实现。

HttpContext.User定义如下

通过User属性可以访问Iprincipal接口的属性和方法。

//获取或设置当前 HTTP 请求的安全信息。
public IPrincipal User
{
get;
[SecurityPermissionAttribute(SecurityAction.Demand, ControlPrincipal = true)]
set;
}

所以扩展用户身份标识可以通过实现IPrincipal接口。

如:

public class MyFormsPrincipal<TUserData> : IPrincipal where TUserData : class, new()
{
private IIdentity _identity;
private TUserData _userData; public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
{
if( ticket == null )
throw new ArgumentNullException("ticket");
if( userData == null )
throw new ArgumentNullException("userData"); _identity = new FormsIdentity(ticket);//Forms身份验证
_userData = userData;
} public TUserData UserData
{
get { return _userData; }
} public IIdentity Identity
{
get { return _identity; }
} public bool IsInRole(string role)
{
return false;
}
}

这个方法的核心是:

  1. 在登录时,创建自定义的FormsAuthenticationTicket对象,它包含了用户信息。
  2. 加密FormsAuthenticationTicket对象。
  3. 创建登录Cookie,它将包含FormsAuthenticationTicket对象加密后的结果。
  4. 在管线的早期阶段,读取登录Cookie,如果有,则解密。
  5. 从解密后的FormsAuthenticationTicket对象中还原我们保存的用户信息。
  6. 设置HttpContext.User为我们自定义的对象。

代码

如有不对,请多多指教。


参考:


Asp.Net MVC 身份验证-Forms的更多相关文章

  1. Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

    本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直 ...

  2. jwt的ASP.NET MVC 身份验证

    Json Web Token(jwt)      一种不错的身份验证及授权方案,与 Session 相反,Jwt 将用户信息存放在 Token 的 payload 字段保存在客户端,通过 RSA 加密 ...

  3. Asp.net Mvc身份验证

    1.安装组件 Microsoft.AspNet.Identity.Core,身份认证核心组件 安装Microsoft.AspNet.Identity.EntityFramework,EF实现身份认证 ...

  4. 转 asp.net mvc 身份验证中返回绝对路径的ReturnUrl

    原文:http://www.cnblogs.com/hyl8218/archive/2011/11/22/2259116.html 从HttpUnauthorizedResult的源码可以看出,Htt ...

  5. ASP.NET MVC 随想录——探索ASP.NET Identity 身份验证和基于角色的授权,中级篇

    在前一篇文章中,我介绍了ASP.NET Identity 基本API的运用并创建了若干用户账号.那么在本篇文章中,我将继续ASP.NET Identity 之旅,向您展示如何运用ASP.NET Ide ...

  6. ASP.NET中身份验证

    ASP.NET中身份验证有三种方式:Windows.Forms和Passport. 1.Windows验证,基于窗体验证,需要每个页面写上验证身份代码,相对灵活,但操作过于复杂: 2.Passport ...

  7. ASP.NET中身份验证的三种方法

    Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验证用的最多,也最灵活.Forms 验证方式对基于用户的验证授权 ...

  8. ASP.NET Identity 身份验证和基于角色的授权

    ASP.NET Identity 身份验证和基于角色的授权 阅读目录 探索身份验证与授权 使用ASP.NET Identity 身份验证 使用角色进行授权 初始化数据,Seeding 数据库 小结 在 ...

  9. .Net MVC 身份验证

    .Net身份验证主要是分为三种 Windows | Forms | Passport ,其中Froms在项目中用的最多. Windows 身份验证 Forms 验证 Passport 验证 1.Win ...

随机推荐

  1. laravel 对查询结果的二次筛选

    假设有表Scores 里面有 id,math,english等字段,现在要求按总分(数据库没有这个字段)来排序或者筛选,用having()方法就可以很方便解决这个问题. $scores = Score ...

  2. 从零开始构建docker基础镜像

    段子 今年基本已经结束了,我问了很多朋友今年挣钱了没?大多朋友都有挣,而且挣得五花八门:有挣个屁的,有挣个锤子的,有挣个毛的,更有甚者挣个妹的,奢侈之极!最恐怖的是挣个鬼的!有的还可以,挣个球,下午我 ...

  3. JavaScript中对日期格式化的新想法.

    其实我们对与日期的显示,也就那么几种,不需要每次都传格式化字符串. 只要告诉函数你想要什么结果就好了,以下是在ios的JavaScript中我新写的日期格式化函数: /** 格式化日期 @param ...

  4. 来腾讯云开发者实验室 学习.NET Core 2.0

    腾讯云开发者实验室为开发者提供了一个零门槛的在线实验平台,开发者实验室提供的能力: 零门槛扫码即可免费领取实验机器,支持使用自有机器参与,实验完成后支持保留实验成果: 在线 WEB IDE 支持 sh ...

  5. Android Things 专题6 完整的栗子:运用TensorFlow解析图像

    文| 谷歌开发技术专家 (GDE) 王玉成 (York Wang) 前面絮叨了这么多.好像还没有一个整体的概念.我们怎样写一个完整的代码呢? 如今深度学习非常火,那我们就在Android Things ...

  6. Android 訪问权限清单

    Android权限设置 概述 权限 说明 訪问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES 读取或写入登记check-in数据库属性表的权限 获取 ...

  7. C++教程之autokeyword的使用

    一.autokeyword的前世 从C语言開始,autokeyword就被当作是一个变量的存储类型修饰符,表示自己主动变量(局部变量).它不能被单独使用,否则编译器会给出警告. #include &l ...

  8. JVM垃圾收集相关经常使用參数

    參 数 描 述 UseSerialGC 虚拟机执行在Client 模式下的默认值,打开此开关后,使用Serial + Serial Old 的收集器组合进行内存回收 UseParNewGC 打开此开关 ...

  9. netty开发教程(一)

    Netty介绍 Netty is an asynchronous event-driven network application framework  for rapid development o ...

  10. ASP.MVC当URL跳转时候参数的安全性

    一个页面跳转到另外一个页面直接将参数写在URL上面并不安全比如 http://XXXXXXXXXXX/meeting/shakeGroup?id=5381&uid=o0En_sj1J0bFgI ...