http://www.cnblogs.com/bomo/p/3309766.html

随笔 - 121  文章 - 0  评论 - 92

【MVC】ASP.NET MVC Forms验证机制

ASP.NET MVC 3

  使用Forms身份验证

身份验证流程

一、用户登录

  1、验证表单:ModelState.IsValid

  2、验证用户名和密码:通过查询数据库验证

  3、如果用户名和密码正确,则在客户端保存Cookie以保存用户登录状态:SetAuthCookie

    1):从数据库中查出用户名和一些必要的信息,并把额外信息保存到UserData中

    2):把用户名和UserData保存到 FormsAuthenticationTicket 票据中

    3):对票据进行加密 Encrypt

    4):将加密后的票据保存到Cookie发送到客户端

  4、跳转到登录前的页面

二、验证登录

  1、在Global中注册PostAuthenticateRequest事件函数,用于解析客户端发过来的Cookie数据

    1):通过 HttpContext.Current.User.Identity 判断用户是否登录(FormsIdentity,IsAuthenticated,AuthenticationType)

    2):从HttpContext 的Request的Cookie中解析出Value,解密得到 FormsAuthenticationTicket  得到UserData

  2、角色验证

    在Action加入 Authorize特性,可以进行角色验证

      在 HttpContext.Current.User 的 IsInRole 方法进行角色认证(需要重写)

下面是代码,以上用到的所有验证的类都进行重载

一、首先是用户用户身份认证的 IPrincipal

  这里抽象出通用属性,定义两个 IPrincipal

    //通用的用户实体
public class MyFormsPrincipal<TUserData> : IPrincipal
where TUserData : class, new()
{
//当前用户实例
public IIdentity Identity { get; private set; }
//用户数据
public TUserData UserData { get; private set; } public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
{
if (ticket == null)
throw new ArgumentNullException("ticket");
if (userData == null)
throw new ArgumentNullException("userData"); Identity = new FormsIdentity(ticket);
UserData = userData;
} //角色验证
public bool IsInRole(string role)
{
var userData = UserData as MyUserDataPrincipal;
if (userData == null)
throw new NotImplementedException(); return userData.IsInRole(role);
} //用户名验证
public bool IsInUser(string user)
{
var userData = UserData as MyUserDataPrincipal;
if (userData == null)
throw new NotImplementedException(); return userData.IsInUser(user);
}
}

  通用实体里面可以存放数据实体,并且把角色验证和用户验证放到了具体的数据实体里面

    //存放数据的用户实体
public class MyUserDataPrincipal : IPrincipal
{
//数据源
private readonly MingshiEntities mingshiDb = new MingshiEntities(); public int UserId { get; set; } //这里可以定义其他一些属性
public List<int> RoleId { get; set; } //当使用Authorize特性时,会调用改方法验证角色
public bool IsInRole(string role)
{
//找出用户所有所属角色
var userroles = mingshiDb.UserRole.Where(u => u.UserId == UserId).Select(u => u.Role.RoleName).ToList(); var roles = role.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
return (from s in roles from userrole in userroles where s.Equals(userrole) select s).Any();
} //验证用户信息
public bool IsInUser(string user)
{
//找出用户所有所属角色
var users = user.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
return mingshiDb.User.Any(u => users.Contains(u.UserName));
} [ScriptIgnore] //在序列化的时候忽略该属性
public IIdentity Identity { get { throw new NotImplementedException(); } }
}

二、用于验证和设置Cookie的 FormsAuthentication

    //身份验证类
public class MyFormsAuthentication<TUserData> where TUserData : class, new()
{
//Cookie保存是时间
private const int CookieSaveDays = 14; //用户登录成功时设置Cookie
public static void SetAuthCookie(string username, TUserData userData, bool rememberMe)
{
if (userData == null)
throw new ArgumentNullException("userData"); var data = (new JavaScriptSerializer()).Serialize(userData); //创建ticket
var ticket = new FormsAuthenticationTicket(
2, username, DateTime.Now, DateTime.Now.AddDays(CookieSaveDays), rememberMe, data); //加密ticket
var cookieValue = FormsAuthentication.Encrypt(ticket); //创建Cookie
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue)
{
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Domain = FormsAuthentication.CookieDomain,
Path = FormsAuthentication.FormsCookiePath,
};
if (rememberMe)
cookie.Expires = DateTime.Now.AddDays(CookieSaveDays); //写入Cookie
HttpContext.Current.Response.Cookies.Remove(cookie.Name);
HttpContext.Current.Response.Cookies.Add(cookie);
} //从Request中解析出Ticket,UserData
public static MyFormsPrincipal<TUserData> TryParsePrincipal(HttpRequest request)
{
if (request == null)
throw new ArgumentNullException("request"); // 1. 读登录Cookie
var cookie = request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie == null || string.IsNullOrEmpty(cookie.Value)) return null; try
{
// 2. 解密Cookie值,获取FormsAuthenticationTicket对象
var ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null && !string.IsNullOrEmpty(ticket.UserData))
{
var userData = (new JavaScriptSerializer()).Deserialize<TUserData>(ticket.UserData);
if (userData != null)
{
return new MyFormsPrincipal<TUserData>(ticket, userData);
}
}
return null;
}
catch
{
/* 有异常也不要抛出,防止攻击者试探。 */
return null;
}
}
}

三、用于验证角色和用户名的Authorize特性

    //验证角色和用户名的类
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
var user = httpContext.User as MyFormsPrincipal<MyUserDataPrincipal>;
if (user != null)
return (user.IsInRole(Roles) || user.IsInUser(Users)); return false;
} protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//验证不通过,直接跳转到相应页面,注意:如果不使用以下跳转,则会继续执行Action方法
filterContext.Result = new RedirectResult("http://www.baidu.com");
}
}

好了,四个类定义完成,接下来是使用

1、首先是登陆

        [HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
          //通过数据库查询验证
var bll= new UserBll();
var userId = bll.Validate(model.UserName, model.Password, HttpContext.Request.UserHostAddress, HttpContext.Request.UserAgent);
if (userId > 0)
{
//验证成功,用户名密码正确,构造用户数据(可以添加更多数据,这里只保存用户Id)
var userData = new MyUserDataPrincipal {UserId = userId}; //保存Cookie
MyFormsAuthentication<MyUserDataPrincipal>.SetAuthCookie(model.UserName, userData, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "提供的用户名或密码不正确。");
}
} // 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(model);
}

二、登陆完成后,是验证,验证之前首先要获得客户端的用户数据(从之前设置的Cookie中解析)

  在全局文件:Global.asax 中添加下面代码

        protected void Application_PostAuthenticateRequest(object sender, System.EventArgs e)
{
var formsIdentity = HttpContext.Current.User.Identity as FormsIdentity;
if (formsIdentity != null && formsIdentity.IsAuthenticated && formsIdentity.AuthenticationType == "Forms")
{
HttpContext.Current.User =
MyFormsAuthentication<MyUserDataPrincipal>.TryParsePrincipal(HttpContext.Current.Request);
}
}

这样就从Request解析出了UserData,下面可以用于验证了

三、在需要验证角色的Action上添加 [MyAuthorize] 特性

        [MyAuthorize(Roles = "User", Users = "bomo,toroto")]
public ActionResult About()
{
return View();
}

  当用户访问该Action时,调用 MyAuthorize 的 AuthorizeCore 方法进行验证, 如果验证成功,则继续执行,如果验证失败,会调用 HandleUnauthorizedRequest方法做相应处理,在MyAuthorize 中可以获得这里定义的 Roles 和 Users 进行验证

【MVC】ASP.NET MVC Forms验证机制的更多相关文章

  1. Asp.Net的Forms验证,解决Cookie和Seesion失效时间

    网站开发中用户验证一般采用Asp.Net的Forms验证,验证票据存储到Cookie的方式. Session方式是将验证信息存储在内存中,如果你使用的虚拟主机给你分配很小的内存,实际上都是如此,那么s ...

  2. ASP.NET MVC Forms验证机制

    ASP.NET MVC 3 使用Forms身份验证 身份验证流程 一.用户登录 1.验证表单:ModelState.IsValid 2.验证用户名和密码:通过查询数据库验证 3.如果用户名和密码正确, ...

  3. [Asp.net mvc]Asp.net mvc 中使用LocalStorage

    目前使用比较多的本地存储方案有比如Flash SharedObject.Google Gears.Cookie.LocalStorage.User Data.Open Database等方案.综合比较 ...

  4. MVC ASP.NET MVC各个版本的区别

    ASP.NET MVC各个版本的区别 Net Framework4.5是不支持安装在window server 2003上,如非装请用net framework4.0; MVC1.0 publsh t ...

  5. spring mvc controller中的参数验证机制(二)

    这里我们介绍以下自定义的校验器的简单的使用示例 一.包结构和主要文件 二.代码 1.自定义注解文件MyConstraint package com.knyel.validator; import ja ...

  6. spring mvc controller中的参数验证机制(一)

    一.验证用到的注解 @Valid 对传到后台的参数的验证 @BindingResult 配合@Valid使用,验证失败后的返回 二.示例 1.传统方式 @PostMapping public User ...

  7. [Asp.net mvc] Asp.net mvc Kendo UI Grid的使用(四)

    有段时间没写博客了,工作状态比较忙,抽空继续总结下Grid的使用,这次主要介绍模板以及其他官网介绍不详尽的使用方法.先Show出数据,然后讲解下.后台代码: public ActionResult O ...

  8. ASP.NET MVC4系列验证机制、伙伴类共享源数据信息(数据注解和验证)

    一,mvc前后台验证 自定义属性标签MyRegularExpression using System; using System.Collections.Generic; using System.C ...

  9. MVC ASP.NET MVC各个版本的区别 (转)

    Net Framework4.5是不支持安装在window server 2003上,如非装请用net framework4.0; MVC1.0 publsh time:2008 IDEV:VS200 ...

随机推荐

  1. JAVAWEB学习总结 HTTPSERVLETRESPONSE对象(二)

    一.HttpServletResponse常见应用--生成验证码 1.1.生成随机图片用作验证码 生成图片主要用到了一个BufferedImage类 步骤: 1. 在内存中创建一张图片 2.得到图片 ...

  2. NSOperation实现线程间通信

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //1.创建队列 NSOperat ...

  3. NSOperationQueue的基本使用

    NSOperationQueue的作用 NSOperation可以调用start方法来执行任务,但默认是同步执行的 如果将NSOperation添加到NSOperationQueue(操作队列)中,系 ...

  4. renderman、arnold及全局光照

    走马观花看了一些实现全局光(global illumination)的文章,都是非实时电影级的.的确可以分为两个阵营,一是pixar的renderman中常用的reyes+点云,感觉pixar一路走来 ...

  5. ios9-通讯录

    在以往的ios开发中,开发者需要获取用户的通讯录信息的时候,往往要使用AddressBook.frame框架,该框架是纯C语言的API,在开发过程中,还需要开发者手动的管理内存,这对于新进开发者很难理 ...

  6. AIX上通过IPSEC进行IP包过滤

    AIX上的IPSEC 在AIX可以通过以下步骤打开IP Security smitty ipsec4 --> Start/Stop IP Security --> Start IP Sec ...

  7. MSP430单片机的两种SPI总线实现方式

    MSP430单片机上的SPI总线的实现方式分为两种:硬件实现和软件实现. 二者的抽象层次不同,硬件实现方式下程序员只需要完成总线协议的寄存器层,即一字节(char,8位二进制)数据,而软件实现方式下程 ...

  8. js之路

    开始记录js学习: udacity,edx上不去: 搞了一个html+css+JavaScript后开始看es6; 犀牛书,js启示录,你不知道的js上中: 第一个库jQuery及源码分析: --

  9. H5移动端中必备技能

    Meta基础知识: H5页面窗口自动调整到设备宽度,并禁止用户缩放页面<meta name="viewport" content="width=device-wid ...

  10. 《Android深度探索HAL与驱动开发》第四章阅读心得

     Android源代码包含了很多内容,可以实现拍照.计算机.日历.相册.拨号器等功能.配置Android源代码下载环境需要如下几步: (1)创建一个用于存放下载脚本文件的目录:  (2)下载repo脚 ...