zoukankan      html  css  js  c++  java
  • 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 进行验证

  • 相关阅读:
    git命令的使用
    动态生成表格的每一行的操作按钮如何获取当前行的index
    js判断一些时间范围是否有重复时间段
    infiniband install driver
    python之pip install
    KVM :vnc 远程控制kvm创建虚拟机
    如何设置UNIX/Linux中新创建目录或文件的默认权限
    python获取报文参考代码
    JAVA命名规范
    oracle常用知识随笔
  • 原文地址:https://www.cnblogs.com/xiashenbin/p/4940330.html
Copyright © 2011-2022 走看看