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 进行验证
ASP.NET MVC Forms验证机制的更多相关文章
- 【MVC】ASP.NET MVC Forms验证机制
http://www.cnblogs.com/bomo/p/3309766.html 随笔 - 121 文章 - 0 评论 - 92 [MVC]ASP.NET MVC Forms验证机制 ASP. ...
- 通过扩展改善ASP.NET MVC的验证机制[实现篇]
原文:通过扩展改善ASP.NET MVC的验证机制[实现篇] 在<使用篇>中我们谈到扩展的验证编程方式,并且演示了本解决方案的三大特性:消息提供机制的分离.多语言的支持和多验证规则的支持, ...
- 通过扩展改善ASP.NET MVC的验证机制[使用篇]
原文:通过扩展改善ASP.NET MVC的验证机制[使用篇] ASP.NET MVC提供一种基于元数据的验证方式是我们可以将相应的验证特性应用到作为Model实体的类型或者属性/字段上,但是这依然具有 ...
- 给Asp.net MVC Forms 验证设置角色访问控制
当我们使用Asp.net MVC Forms方式验证用户, 然后设置Controller 或 Action 的 Authorize属性时, 默认情况下只有Users属性可以设置(这里的Users通常是 ...
- Asp.Net MVC 身份验证-Forms
Asp.Net MVC 身份验证-Forms 在MVC中对于需要登录才可以访问的页面,只需要在对应的Controller或Action上添加特性[Authorize]就可以限制非登录用户访问该页面.那 ...
- ASP.NET MVC Model验证(一)
ASP.NET MVC Model验证(一) 前言 前面对于Model绑定部分作了大概的介绍,从这章开始就进入Model验证部分了,这个实际上是一个系列的Model的绑定往往都是伴随着验证的.也会在后 ...
- ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)
在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie ...
- Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码
本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直 ...
- ASP.NET MVC异步验证是如何工作的03,jquery.validate.unobtrusive.js是如何工作的
在上一篇"ASP.NET MVC异步验证是如何工作的02,异步验证表单元素的创建"中了解了ASP.NET异步验证是如何创建表单元素的,本篇体验jquery.validate.uno ...
随机推荐
- Memocache
http://blog.csdn.net/zhoufoxcn/article/details/6282099 http://blog.csdn.net/dinglang_2009/article/de ...
- 一次HBase问题的解决过程(Status: INCONSISTENT)
==版本信息== HBase:2.7.1 Storm:1.0.1 RocketMQ:3.4.6(阿里版) ==问题描述== 2018年9月3号晚上23点左右,例行巡检系统运行状况时, 发现Storm消 ...
- 经典递归问题:0,1背包问题 kmp 用遗传算法来解背包问题,hash表,位图法搜索,最长公共子序列
0,1背包问题:我写笔记风格就是想到哪里写哪里,有很多是旧的也没删除,代码内部可能有很多重复的东西,但是保证能运行出最后效果 '''学点高大上的遗传算法''' '''首先是Np问题的定义: npc:多 ...
- 2018.09.24 bzoj1016: [JSOI2008]最小生成树计数(并查集+搜索)
传送门 正解是并查集+矩阵树定理. 但由于数据范围小搜索也可以过. 我们需要知道最小生成树的两个性质: 不同的最小生成树中,每种权值的边出现的个数是确定的 不同的生成树中,某一种权值的边连接完成后,形 ...
- 第六章 副词(Les adverbes )
副词属于不变词类,无性.数变化(tout除外),它的功能是修饰动词.形容词.副词或句子. ➡副词的构成 ⇨单一副词 bien tard hier mal vite tôt très souvent ...
- class和struct
相同点 实际上可以使用这两个关键字定义任何一个类. 区别 1.struct的默认成员访问说明符为public,class的默认成员访问说明符为private(什么叫默认?就是没有写明public.pr ...
- Windows10和CentOS7双系统安装的一些小技巧
我个人是先安装好了win10系统,且win10是单独在一个120g的盘里:而centOS7则是安装在另一个500g的磁盘的其中的380g里: 这里要着重注意的是,500g里分成380g的盘不要在win ...
- springmvc 开涛 注解式控制器
版本 定义处理器类 处理器映射适配器 备注 支持的注解 2.5前 controller 2.5 注解 DefaultAnnotationHandlerMapping AnnotationM ...
- 百分之 95% 的程序员不知道 Trending 是什么。
前言如果学习到的知识不成体系,那么遇到问题时就会非常难解决.常有人问你从哪里了解新技术怎么判断其发展趋势的,除了关注 Hacker News 以及庞大的 Awesome 还有没有其它方式?有啊当然是每 ...
- 曲演杂坛--HASH的一点理解
HASH,百度百科上做如下定义: Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列 ...