MVC 登录认证与授权及读取登录错误码

简介: 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。   十年河东十年河西,莫欺少年穷   学无止境,精益求精   最近在自学MVC,遇到的问题很多,索性一点点总结下。

   最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。

   十年河东十年河西,莫欺少年穷

   学无止境,精益求精

   最近在自学MVC,遇到的问题很多,索性一点点总结下。

   正题:

   做过三层架构的童鞋都知道,如果要验证/授权一个用户登录,我们一般采取如下方法:

   1、用户输入用户名、密码进行登录

   2、账户/密码正确,保存用户信息至Cookies或者Session,跳转至主页面

   3、在主页面继承BasePage类,并在Page_Load的时候,判断Cookies或者Session的值,如果Cookies或者Session中存放的值在数据库验证成功,则可以观看主页面信息,如果验证不成功,或者Session/Cookies值为NULL时,直接执行语句:Response.Redirect(../Login.aspx);

   上述方法很简单,功能上也实现了登录的认证与授权,相信很多人写的项目都会采取此方法进行判断,殊不知:这种方法安全系数较低,而且违背了NET的登录授权与验证原则。那么NET中的授权与验证又是什么呢?

   .net中的认证(authentication)与授权(authorization)

   认证(authentication) 就是 :判断用户有没有登录,用户输入的账户密码等信息是否通过数据库比对

   授权(authorization) 就是:用户登录后的身份/角色识别,用户通过认证后<登陆后>我们记录用户的信息并授权

   .net中与"认证"对应的是IIdentity接口,而与"授权"对应的则是IPrincipal接口,这二个接口的定义均在命名空间System.Security.Principal中,详情如下:

   用户认证接口:

using System;
using System.Runtime.InteropServices;
 
namespace System.Security.Principal
{
    [ComVisible(true)]
    public interface IIdentity
    {
           string AuthenticationType { get; }
           bool IsAuthenticated { get; }
           string Name { get; }
    }
}

   用户授权接口:

using System;
using System.Runtime.InteropServices;
 
namespace System.Security.Principal
{
    [ComVisible(true)]
    public interface IPrincipal
    {
          IIdentity Identity { get; }
          bool IsInRole(string role);
    }
}

   应该注意到:IPrincipal接口中包含着一个只读的IIdentity

   1、IIdentity 接口中属性的含义:

   AuthenticationType  验证方式:NET中有Windows、Forms、Passport三种验证方式,其中以Forms验证用的最多,本节讲解的MVC验证与授权就是基于Form。

   IsAuthenticated  是否通过验证,布尔值

   Name  用户登录的账户名称

   2、IPrincipal 接口中属性的含义:

   IIdentity  上述IIdentity接口的实例

   IsInRole  布尔值,验证用户权限

   下面我们对HttpContext封闭类刨根问底

   

   HttpContext.User本身就是一个IPrincipal接口的实例。

   有了上面的预备知识,我们在程序中处理认证问题时,只需对HttpContext.User赋值即可。

   下面是小弟模拟的一个授权方法

        /// <summary>
        /// 模拟授权  此处不经过验证 直接授权访问 UserCenter() 
        /// </summary>
        /// <returns></returns>
        public ActionResult Login()
        {
            GenericIdentity _identity = new GenericIdentity("天才卧龙", "Forms");
            GenericPrincipal _principal = new GenericPrincipal(_identity, new string[] { "admins", "vips" });
            HttpContext.User = _principal;
            return RedirectToAction("UserCenter");
        }

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

   上面的方法中,一旦运行登陆页,不需要填账户密码,直接会访问UserCenter(),即:直接会跳转到个人中心

   好啦,如果上边的例子大家看懂了,那么下面的登录其实就很简单了,和上述所讲一样:由于 HttpContext.User 本身就是一个IPrincipal接口的实例!因此:我们只需给这个实例赋值即可,上面的例子就证实了这一点。

   咱们回到开篇的描述,一般我们都是通过Session或者cookies就行Forms验证登录,那么在MVC中我们应当怎么做呢?

   1.MVC的Forms验证

   Forms验证在内部的机制为把用户数据加密后保存在一个基于cookie的票据FormsAuthenticationTicket中,因为是经过特殊加密的,所以应该来说是比较安全的。而.net除了用这个票据存放自己的信息外,还留了一个地给用户自由支配,这就是现在要说的UserData。

   UserData可以用来存储string类型的信息,并且也享受Forms验证提供的加密保护,当我们需要这些信息时,也可以通过简单的get方法得到,兼顾了安全性和易用性,用来保存一些必须的敏感信息还是很有用的。

   在此,本案例中UserData用于存储用户角色值,admins/vips/superadmins等,这些值大家可根据系统需求自行定义。本案例中是写死的,当然,在实际项目中应当根据登录账户信息结合数据库进行赋值。

  2、FormsAuthenticationTicket基于forms的验证

   构建基于forms的验证机制过程如下: 
 1,设置IIS为可匿名访问和asp.net web.config中设置为form验证,配置webConfig,如下:

    <!--用户没有授权时 自动退回登陆界面-->
    <authentication mode="Forms">
      <forms loginUrl="~/Home/Login" timeout="2880" path="/" />
    </authentication>

 2,检索数据存储验证用户,并检索角色(其实就是登录验证) 
 3,使用FormsAuthenticationTicket创建一个Cookie并回发到客户端,并存储 

   关于2、3两步,我以代码示例:

   我创建的登录Model如下:

public class UserLogin
    {
        private readonly object LOCK = new object();
        Maticsoft.BLL.YX_UserInfo Bll = new Maticsoft.BLL.YX_UserInfo();
        Maticsoft.Model.YX_UserInfo Mol = new Maticsoft.Model.YX_UserInfo();
        //
        [Required(ErrorMessage = "请输入账户号码/手机号")]
        [RegularExpression(@"^1[3458][0-9]{9}$", ErrorMessage = "手机号格式不正确")]
        public string UserName { get; set; }

        [Required(ErrorMessage="请输入账户密码")]
        [DataType(DataType.Password,ErrorMessage="密码格式不正确")]
        [MinLength(6, ErrorMessage = "密码长度介于6~15位之间")]
        [MaxLength(15, ErrorMessage = "密码长度介于6~15位之间")]
        public string UserPwd { get; set; }

        public bool LoginAction()
        {
            lock (LOCK)
            {
                DataTable dt = Bll.GetList(" Uname='" + CommonMethod.CheckParamThrow(UserName) + "' and Upwd='" + CommonMethod.Md532(UserPwd) + "' and flat1=1").Tables[0];
                if (dt.Rows.Count > 0)
                {
                    FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                           1,
                           UserName,
                           DateTime.Now,
                           DateTime.Now.AddMinutes(30),
                           false,
                           "admins,vip",
                           "/"
                           );
                    string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
                    System.Web.HttpCookie authCookie = new System.Web.HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
                    System.Web.HttpContext.Current.Response.Cookies.Add(authCookie);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }

   上述代码中

   

   数据库验证成功,我们将用户信息存入Cookies,

   然后我们在Global中解析这个Cookies,然后把解析的结果赋值给:HttpContext.User

   代码如下:

        /// <summary>
        /// 登录验证、s授权
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_AuthenticateRequest(Object sender, EventArgs e)
        {
            string cookieName = FormsAuthentication.FormsCookieName;
            HttpCookie authCookie = Context.Request.Cookies[cookieName];
            FormsAuthenticationTicket authTicket = null;
            try
            {
                authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            }
            catch (Exception ex)
            {
                return;
            }
            string[] roles = authTicket.UserData.Split(',');
            FormsIdentity id = new FormsIdentity(authTicket);
            GenericPrincipal principal = new GenericPrincipal(id, roles);
            Context.User = principal;//存到HttpContext.User中     
        }

   Context.User一旦被赋值成功,我们就可以访问那些有权限限制的方法。代码如下:

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

   SO,搞懂了本质,一切都可以解决。

   补充:本人登录代码如下:

   前端 Login.cshtml

@{
    ViewBag.Title = "登录";
}
@section css
{
    <link href="~/Content/login.css" rel="stylesheet" />
}
<form id="form1">
<div class="wrap loginBox">
    <div class="logo"><img src="/images/logo.png"/></div>
    <div class="head">
        <ul>
            <li><input type="text" id="UserName" name="UserName" class="text n2" placeholder="手机号码" /></li>
            <li><input type="password" id="UserPwd" name="UserPwd" class="text n3" placeholder="密码" /></li>
        </ul>
    </div>
    <div class="body">
        <a href="JavaScript:void(0)" class="btn btn-blue" onclick="Login()">立即登录</a>
        @*<a href="JavaScript:void(0)" class="btn btn-yell">逗包账号登录</a>*@
    </div>
    <div class="foot">
        <a href="JavaScript:void(0)">忘记密码?</a><a href="JavaScript:void(0)">账号注册</a>
    </div>
</div>
</form>
<script type="text/javascript">
    function Login() {
        var UserName = $("#UserName").val();
        var UserPwd = $("#UserPwd").val();
        $(document).ready(function (data) {
            $.ajax({
                url: "/home/Login",
                type: "post",
                contentType: "application/json",
                dataType: "text",
                data: JSON.stringify({ UserName: UserName, UserPwd: UserPwd }),
                success: function (result, status) {
                    if (result == "200") {
                        //登录成功 跳转
                        location.href = "http://www.baidu.com";
                    }
                    else {
                        alert(result);//弹出错误码
                    }
                },
                error: function (error) {
                    alert(error);
                }
            });
        });
    }
</script>

   后端 HomeController.cs:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return RedirectToAction("Login");
        }

        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        public object Login(UserLogin LoginMol)
        {
            if (NetworkHelper.Ping())//判断当前是否有网络
            {
                if (ModelState.IsValid)//是否通过Model验证
                {
                    if (LoginMol.LoginAction())
                    {
                        return 200;
                    }
                    else
                    {
                        return "账户或密码有误";
                    }
                }
                else
                {
                    //读取错误信息
                    StringBuilder sb = new StringBuilder("");
                    var errors = ModelState.Values;
                    foreach (var item in errors)
                    {
                        foreach (var item2 in item.Errors)
                        {
                            if (!string.IsNullOrEmpty(item2.ErrorMessage))
                            {
                                if (string.IsNullOrEmpty(sb.ToString()))
                                {
                                    sb.AppendLine(item2.ErrorMessage);//读取一个错误信息 并返回
                                }
                            }
                        }
                    }
                    return sb.ToString();
                }
            }
            else
            {
                return "当前无网络,请稍后重试";
            }
        }
    }

   上述既是完整登录代码,谢谢!

   @陈卧龙的博客

相关文章
|
存储 前端开发 Java
Spring MVC 实战:三种方式获取登录用户信息
前言 Web 项目中,维持用户登录状态的常用方式有三种,分别是 Cookie、Session、Token,不管哪种方案,都需要获取到用户信息供业务层使用。
358 0
Spring MVC 实战:三种方式获取登录用户信息
|
XML 前端开发 数据处理
Android——MVC、MVP、MVVM框架实现登录示例
MVC 描述 缺点 优点 MVP 效果图 描述 缺点 优点 代码解析 视图效果图 建立实体类 建立实体类接口 实现实体类接口 设置P层 建立交互接口 数据绑定 MVVM 效果图 描述 代码解析 导入dataBinding 实体类 建立viewmodel xml绑定数据 视图与数据绑定
354 0
Android——MVC、MVP、MVVM框架实现登录示例
|
JSON 前端开发 C#
C# MVC 实现登录的5种方式
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。    十年河东十年河西,莫欺少年穷。     学无止境,精益求精    小弟之前做过三月的MVC,后来又一直webForm开发,再后来,也就是现在做WPF,最近随着项目进入尾声,也就慢慢清闲了下来,清...
1958 0
|
前端开发 数据安全/隐私保护