MVC 登錄認證與授權及讀取登錄錯誤碼


   最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。

   十年河東十年河西,莫欺少年窮

   學無止境,精益求精

   最近在自學MVC,遇到的問題很多,索性一點點總結下。

[AllowAnonymous] 用於無需授權登錄的控制器

   正題:

   做過三層架構的童鞋都知道,如果要驗證/授權一個用戶登錄,我們一般采取如下方法:

   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 "當前無網絡,請稍后重試";
            }
        }
    }

   上述既是完整登錄代碼,謝謝!

   @陳卧龍的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM