程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證

C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證

編輯:C#入門知識

C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證。本站提示廣大學習愛好者:(C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證)文章只能為提供參考,不一定能成為您想要的結果。以下是C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證正文


媒介:比來,評論辯論到數據庫平安的成績,因而就引出了WebApi辦事沒有加任何驗證的成績。也就是說,任何人只需曉得了接口的url,都可以或許模仿http要求去拜訪我們的辦事接口,從而去增刪改查數據庫,這效果想一想都恐懼。經由一番折騰,總算是加上了接口的身份認證,在此記載下,也給須要做身份認證的園友們供給參考。

1、為何須要身份認證

在媒介外面,我們說了,假如沒有啟用身份認證,那末任何匿名用戶只需曉得了我們辦事的url,就可以隨便拜訪我們的辦事接口,從而拜訪或修正數據庫。

1、我們不加身份認證,匿名用戶可以直接經由過程url隨便拜訪接口:

 

可以看到,匿名用戶直接經由過程url就可以拜訪我們的數據接口,終究會產生甚麼事,年夜家可以隨便暢想。

2、增長了身份認證以後,只要帶了我們拜訪單子的要求能力拜訪我們的接口。

例如我們直接經由過程url拜訪,會前往401

 

 假如是正常流程的要求,帶了單子,就OK了。

可以看到,正常流程的要求,會在要求報文的頭外面增長Authorization這一項,它的值就是我們的Ticket單子信息。

2、Basic基本認證的道理解析

1、罕見的認證方法

我們曉得,asp.net的認證機制有許多種。關於WebApi也不破例,罕見的認證方法有

  • FORM身份驗證
  • 集成WINDOWS驗證
  • Basic基本認證
  • Digest摘要認證

園子裡許多關於WebApi認證的文章,各類認證方法都邑觸及到,但感到都不敷細。這裡也其實不想去研討哪一種驗證方法實用哪一種應用場景,由於博主照樣認為“貪多嚼不爛”,也能夠是博主才能所限。關於認證機制,弄懂個中一種,其他的都能融合貫穿。此篇就應用Basic基本認證來具體講授下全部的進程。

2、Basic基本認證道理

 我們曉得,認證的目標在於平安,那末若何能包管平安呢?經常使用的手腕天然是加密。Basic認證也不破例,重要道理就是加密用戶信息,生成單子,每次要求的時刻將單子帶過去驗證。如許說能夠有點籠統,我們具體分化每一個步調:

  1. 起首上岸的時刻驗證用戶名、暗碼,假如上岸勝利,則將用戶名、暗碼依照必定的規矩生成加密的單子信息Ticket,將單子信息前往到前端。
  2. 假如上岸勝利,前端會收到單子信息,然後跳轉到主界面,而且將單子信息也帶到主界面的ActionResult外面(例如跳轉的url可以如許寫:/Home/Index?Ticket=Ticket)
  3. 在主界面的ActionResult外面經由過程參數獲得單子信息Ticket,然後將Ticket信息保留到ViewBag外面傳到前端。
  4. 在主界面的前端,發送Ajax要求的時刻將單子信息參加到要求的Head外面,將單子信息跟著要求一路發送到辦事端去。
  5. 在WebApi辦事外面界說一個類,繼續AuthorizeAttribute類,然後重寫父類的OnAuthorization辦法,在OnAuthorization辦法外面取到以後http要求的Head,從Head外面取到我們前端傳過去的單子信息。解密單子信息,從解密的信息外面獲得用戶名和暗碼,然後驗證用戶名和暗碼能否准確。假如准確,表現驗證經由過程,不然前往未驗證的要求401。
  6.  這個根本的道理。上面就依照這個道理來看看每步的代碼若何完成。

    3、Basic基本認證的代碼示例

    起首說下我們的示例場景,前次引見 CORS 的時刻我們在一個處理計劃外面放了兩個項目Web和WebApiCORS,我們此次照樣以這個為例來講明。

    1、登錄進程1.1、Web前端

    <body>
      <div > 
        <div>用戶名:<input type="text" id="txt_username" /></div>
        <div>密 碼:<input type="password" id="txt_password" /></div>
        <div><input type="button" value="登錄" id="btn_login" class="btn-default" /></div>
      </div>
    </body>
    $(function () {
      $("#btn_login").click(function () {
        $.ajax({
          type: "get",
          url: "http://localhost:27221/api/User/Login",
          data: { strUser: $("#txt_username").val(), strPwd: $("#txt_password").val() },
          success: function (data, status) {
            if (status == "success") {
              if (!data.bRes){
                alert("登錄掉敗");
                return;
              }
              alert("登錄勝利");
                //登錄勝利以後將用戶名和用戶單子帶到主界面
              window.location = "/Home/Index?UserName=" + data.UserName + "&Ticket=" + data.Ticket;
            }
          },
          error: function (e) {
          },
          complete: function () {
    
          }
    
        });
      });
    });

    1.2、登錄的API接口

      public class UserController : ApiController
      {
        /// <summary>
        /// 用戶登錄
        /// </summary>
        /// <param name="strUser"></param>
        /// <param name="strPwd"></param>
        /// <returns></returns>
        [HttpGet]
        public object Login(string strUser, string strPwd)
        {
          if (!ValidateUser(strUser, strPwd))
          {
            return new { bRes = false };
          }
          FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(0, strUser, DateTime.Now,
                  DateTime.Now.AddHours(1), true, string.Format("{0}&{1}", strUser, strPwd),
                  FormsAuthentication.FormsCookiePath);
          //前往登錄成果、用戶信息、用戶驗證單子信息
          var oUser = new UserInfo { bRes = true, UserName = strUser, Password = strPwd, Ticket = FormsAuthentication.Encrypt(ticket) };
          //將身份信息保留在session中,驗證以後要求能否是有用要求
          HttpContext.Current.Session[strUser] = oUser;
          return oUser;
        }
    
        //校驗用戶名暗碼(正式情況中應當是數據庫校驗)
        private bool ValidateUser(string strUser, string strPwd)
        {
          if (strUser == "admin" && strPwd == "123456")
          {
            return true;
          }
          else
          {
            return false;
          }
        }
      }
    
      public class UserInfo
      {
        public bool bRes { get; set; }
    
        public string UserName { get; set; }
    
        public string Password { get; set; }
    
        public string Ticket { get; set; }
      }

    這裡有一點須要留意的是,由於WebApi默許是沒有開啟Session的,所以須要我們作一下設置裝備擺設,手動去啟用session。

    正如下面的道理部門說的,登錄假如掉敗,則直接前往;假如勝利,則將生成的單子Ticket帶到前端,傳到主界面/Home/Index,上面,我們就來看看主界面Home/Index。

    2、/Home/Index主界面

      public class HomeController : Controller
      {
        // GET: Home
        public ActionResult Index(string UserName, string Ticket)
        {
          ViewBag.UserName = UserName;
          ViewBag.Ticket = Ticket;
          return View();
        }
      }
    <html>
    <head>
      <meta name="viewport" content="width=device-width" />
      <title>Index</title>
      <script src="~/Content/jquery-1.9.1.js"></script>
      <link href="~/Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
      <script src="~/Content/bootstrap/js/bootstrap.js"></script>
      <script src="~/Scripts/Home/Index.js"></script>
      <script type="text/javascript">
        //翻開頁面的時刻保留單子信息
        var UserName = '@ViewBag.UserName';
        var Ticket = '@ViewBag.Ticket';
      </script>
    </head>
    <body>
      <div>以後登錄用戶:'@ViewBag.UserName'</div>
    
      <div id="div_test">
    
      </div>
    </body>
    </html>
    $(function () {
      $.ajax({
        type: "get",
        url: "http://localhost:27221/api/Charging/GetAllChargingData",
        data: {},
        beforeSend: function (XHR) {
          //發送ajax要求之前向http的head外面參加驗證信息
          XHR.setRequestHeader('Authorization', 'BasicAuth ' + Ticket);
        },
        success: function (data, status) {
          if (status == "success") {
            $("#div_test").html(data);
          }
        },
        error: function (e) {
          $("#div_test").html("Error");
        },
        complete: function () {
    
        }
    
      });
    });

    這裡須要解釋的是,我們在發送ajax要求之前,經由過程 XHR.setRequestHeader('Authorization', 'BasicAuth ' + Ticket); 這一句向要求的報文頭外面增長單子信息。就是由於這裡加了這一句,所以才有我們下圖中的紅線部門:

    3、WebApiCORS驗證部門(重點)

    我們看到,下面的/Home/Index頁面外面發送了ajax要求去拜訪辦事的 http://localhost:27221/api/Charging/GetAllChargingData 這個接口,那末我們在WebApi外面怎樣去驗證這個要求和正當的要求呢?接上去我們重點看看驗證的這個進程。

    3.1、在WebApiCORS項目外面自界說一個類RequestAuthorizeAttribute,去繼續我們的AuthorizeAttribute這個類。然後重寫OnAuthorization辦法,在這個辦法外面取到要求頭的Ticket信息,然後校驗用戶名暗碼能否公道。

       /// <summary>
      /// 自界說此特征用於接口的身份驗證
      /// </summary>
      public class RequestAuthorizeAttribute : AuthorizeAttribute
      {
        //重寫基類的驗證方法,參加我們自界說的Ticket驗證
        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
          //從http要求的頭外面獲得身份驗證信息,驗證能否是要求提議方的ticket
          var authorization = actionContext.Request.Headers.Authorization;
          if ((authorization != null) && (authorization.Parameter != null))
          {
            //解密用戶ticket,並校驗用戶名暗碼能否婚配
            var encryptTicket = authorization.Parameter;
            if (ValidateTicket(encryptTicket))
            {
              base.IsAuthorized(actionContext);
            }
            else
            {
              HandleUnauthorizedRequest(actionContext);
            }
          }
          //假如取不到身份驗證信息,而且不許可匿名拜訪,則前往未驗證401
          else
          {
            var attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>();
            bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute);
            if (isAnonymous) base.OnAuthorization(actionContext);
            else HandleUnauthorizedRequest(actionContext);
          }
        }
    
        //校驗用戶名暗碼(正式情況中應當是數據庫校驗)
        private bool ValidateTicket(string encryptTicket)
        {
          //解密Ticket
          var strTicket = FormsAuthentication.Decrypt(encryptTicket).UserData;
    
          //從Ticket外面獲得用戶名和暗碼
          var index = strTicket.IndexOf("&");
          string strUser = strTicket.Substring(0, index);
          string strPwd = strTicket.Substring(index + 1);
    
          if (strUser == "admin" && strPwd == "123456")
          {
            return true;
          }
          else
          {
            return false;
          }
        }
      }

    3.2、在詳細的Api接口增長我們下面自界說類的特征

    [RequestAuthorize]
      public class ChargingController : ApiController
      {
        /// <summary>
        /// 獲得一切數據
        /// </summary>
        /// <returns>前往數據</returns>
        [HttpGet]
        public string GetAllChargingData()
        {
          return "Success";
        }
    
        /// <summary>
        /// 獲得以後Id的一切數據
        /// </summary>
        /// <param name="id">參數Id</param>
        /// <returns>前往數據</returns>
        [HttpGet]
        public string GetAllChargingData(string id)
        {
          return "ChargingData" + id;
        }
    
      }

    增長了特征標注以後,每次要求這個API外面的接口之前,法式會先輩入到我們override過的 OnAuthorization() 辦法外面,驗證經由過程以後,才會進到響應的辦法外面去履行,不然前往401。

    4、優化
     經由過程下面的幾步,根本就可以到達我們想要的身份認證的後果,然則老是感到不太便利,重要不太便利的點有以下幾個。

    1.每次新建一個API,對應的接口下面都要標注 [RequestAuthorize] 這個一個器械,感到好費事。
    2.每次發送ajax要求,都要在beforeSend事宜外面加 XHR.setRequestHeader('Authorization', 'BasicAuth ' + Ticket); 這個,感到也費事。
    關於以上兩點,我們優化下

    1、處理API的成績
    在API外面加一個公共的父類,在父類下面標注 [RequestAuthorize] 便可。

    namespace WebApiCORS.Controllers
    {
      [RequestAuthorize]
      [EnableCors(origins: "*", headers: "*", methods: "*")]
      public class BaseApiController : ApiController
      {
      }
    }
    namespace WebApiCORS.Controllers
    {
      public class ChargingController : BaseApiController
      {
        /// <summary>
        /// 獲得一切數據
        /// </summary>
        /// <returns>前往數據</returns>
        [HttpGet]
        public string GetAllChargingData()
        {
          return "Success";
        }
    
        /// <summary>
        /// 獲得以後Id的一切數據
        /// </summary>
        /// <param name="id">參數Id</param>
        /// <returns>前往數據</returns>
        [HttpGet]
        public string GetAllChargingData(string id)
        {
          return "ChargingData" + id;
        }
      }
    }

     留意:我們登錄的要求是不須要驗證的,由於登錄的時刻還沒有發生單子,所以登錄的API不克不及夠繼續 BaseApiController

    2、處理ajax的成績
    還記得我們在 JS組件系列——封裝本身的JS組件,你也能夠 這篇外面引見的增長ajax的error事宜的公共處置辦法嗎?我們能否也能夠經由過程異樣的機制去增長這個呢。新建一個文件Jquery_ajax_extention.js

    (function ($) {
      //1.獲得$.ajax的對象
      var _ajax = $.ajax;
      $.ajax = function (options) {
        //2.每次挪用發送ajax要求的時刻界說默許的error處置辦法
        var fn = {
          error: function (XMLHttpRequest, textStatus, errorThrown) {
            toastr.error(XMLHttpRequest.responseText, '毛病新聞', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
          },
          success: function (data, textStatus) { },
          beforeSend: function (XHR) { },
          complete: function (XHR, TS) { }
        }
        //3.擴大原生的$.ajax辦法,前往最新的參數
        var _options = $.extend({}, {
          error: function (XMLHttpRequest, textStatus, errorThrown) {
            fn.error(XMLHttpRequest, textStatus, errorThrown);
          },
          success: function (data, textStatus) {
            fn.success(data, textStatus);
          },
          beforeSend: function (XHR) {
            XHR.setRequestHeader('Authorization', 'BasicAuth ' + Ticket);
            fn.beforeSend(XHR);
          },
          complete: function (XHR, TS) {
            fn.complete(XHR, TS);
          }
        }, options);
        //4.將最新的參數傳回ajax對象
        _ajax(_options);
      };
    })(jQuery);

    援用這個js後再發送ajax不用在每一個要求的beforeSend外面寫了。

    5、總結
    以上聯合一個實例講授了下Basic認證的完成道理和簡略應用,本文不雅點都是來自博主本身的懂得,假如有不周全的處所,還望園友們示正。假如本文可以或許或多或少幫到你,無妨協助推舉

    以上這篇C#進階系列 WebApi身份認證處理計劃推舉:Basic基本認證就是小編分享給年夜家的全體內容了,願望能給年夜家一個參考,也願望年夜家多多支撐。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved