亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#進階系列 WebApi身份認證解決方案推薦:Basic基礎認證

2020-01-24 01:13:58
字體:
來源:轉載
供稿:網友

前言:最近,討論到數據庫安全的問題,于是就引出了WebApi服務沒有加任何驗證的問題。也就是說,任何人只要知道了接口的url,都能夠模擬http請求去訪問我們的服務接口,從而去增刪改查數據庫,這后果想想都恐怖。經過一番折騰,總算是加上了接口的身份認證,在此記錄下,也給需要做身份認證的園友們提供參考。

一、為什么需要身份認證

在前言里面,我們說了,如果沒有啟用身份認證,那么任何匿名用戶只要知道了我們服務的url,就能隨意訪問我們的服務接口,從而訪問或修改數據庫。

1、我們不加身份認證,匿名用戶可以直接通過url隨意訪問接口:

 

可以看到,匿名用戶直接通過url就能訪問我們的數據接口,最終會發生什么事,大家可以隨意暢想。

2、增加了身份認證之后,只有帶了我們訪問票據的請求才能訪問我們的接口。

例如我們直接通過url訪問,會返回401

 

 如果是正常流程的請求,帶了票據,就OK了。

可以看到,正常流程的請求,會在請求報文的頭里面增加Authorization這一項,它的值就是我們的Ticket票據信息。

二、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。

 這個基本的原理。下面就按照這個原理來看看每一步的代碼如何實現。

三、Basic基礎認證的代碼示例

首先說下我們的示例場景,上次介紹 CORS 的時候我們在一個解決方案里面放了兩個項目Web和WebApiCORS,我們這次還是以這個為例來說明。

1、登錄過程1.1、Web前端

<body> <div style="text-align:center;">   <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。,請參考:http://www.49028c.com/article/143197.htm

正如上面的原理部分說的,登錄如果失敗,則直接返回;如果成功,則將生成的票據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。

四、優化
 通過上面的幾步,基本就能達到我們想要的身份認證的效果,但是總是感覺不太方便,主要不太方便的點有以下幾個。

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里面寫了。

五、總結
以上結合一個實例講解了下Basic認證的實現原理以及簡單使用,本文觀點都是來自博主自己的理解,如果有不全面的地方,還望園友們斧正。如果本文能夠或多或少幫到你,不妨幫忙推薦

以上這篇C#進階系列 WebApi身份認證解決方案推薦:Basic基礎認證就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品国产一区二区电影| 91亚洲精品一区二区| 免费91麻豆精品国产自产在线观看| 91视频国产精品| 国产精品久久视频| 亚洲欧美成人在线| 97av在线播放| 久久精品成人动漫| 麻豆国产va免费精品高清在线| 欧美电影在线观看高清| 精品中文字幕在线2019| 91亚洲精品视频| 亚洲精品www久久久| 亚洲精品自拍第一页| 热久久美女精品天天吊色| 欧美成人午夜激情视频| 97在线视频免费播放| 欧美自拍大量在线观看| 国产一区二区美女视频| 国产91色在线免费| 久久国产精品99国产精| 日韩成人在线视频网站| 亚洲国产婷婷香蕉久久久久久| 欧美国产日韩二区| 日本久久久久亚洲中字幕| 日日狠狠久久偷偷四色综合免费| 亚洲人成伊人成综合网久久久| 国产啪精品视频网站| www.久久久久久.com| 欧美高清电影在线看| 精品国产乱码久久久久酒店| 亚洲精品久久久久久久久久久| 久久久久久一区二区三区| 欧美激情第99页| 欧美激情精品久久久久| 国产精品99久久久久久白浆小说| 精品一区二区电影| 亚洲欧美在线一区二区| 国产精品亚洲美女av网站| 亚洲视频777| 国产成人aa精品一区在线播放| 亚洲www永久成人夜色| 中文字幕亚洲综合| 日韩av中文字幕在线| 亚洲精品视频在线观看视频| 在线视频日韩精品| 成人黄色大片在线免费观看| 欧美国产欧美亚洲国产日韩mv天天看完整| 97热在线精品视频在线观看| 国产在线999| 一区二区欧美久久| 一区二区三区天堂av| 色悠久久久久综合先锋影音下载| 欧美高跟鞋交xxxxhd| 亚洲精品www久久久久久广东| 日本精品一区二区三区在线| 日韩欧美中文字幕在线播放| 亚洲午夜性刺激影院| 亚洲天堂第一页| 欧美大奶子在线| 九九热这里只有精品免费看| 成人做爰www免费看视频网站| 久久精品99久久久香蕉| 中文字幕亚洲欧美日韩高清| 91精品国产91久久久久| 欧美富婆性猛交| 日本欧美一二三区| 成人性生交xxxxx网站| 亚洲黄在线观看| 欧美极品少妇与黑人| 国内精品模特av私拍在线观看| 亚洲片av在线| 国产精品香蕉国产| 色偷偷偷综合中文字幕;dd| 在线视频亚洲欧美| 国产高清在线不卡| 欧美亚洲日本网站| 欧美性理论片在线观看片免费| 日韩精品在线私人| 日韩欧美亚洲范冰冰与中字| 久久久久久这里只有精品| 日韩精品在线免费观看视频| 国产精品久久久久久久9999| 免费av一区二区| 热久久美女精品天天吊色| 91在线观看免费| 亚洲欧美日韩成人| 国产精品欧美久久久| 欧美精品久久久久| 精品国产欧美成人夜夜嗨| 中文字幕自拍vr一区二区三区| 黄色91在线观看| 午夜精品久久17c| 日韩av在线影视| 欧美国产精品人人做人人爱| 国模精品视频一区二区三区| 日韩av影视综合网| 97福利一区二区| 欧美精品videos性欧美| 欧美一乱一性一交一视频| 欧美一级片免费在线| 精品久久久91| 日本精品免费观看| 色婷婷av一区二区三区在线观看| 中日韩美女免费视频网址在线观看| 国产丝袜一区二区| 91av在线看| 欧美激情一级精品国产| 欧美激情区在线播放| 国产精品亚洲美女av网站| 亚洲第一网站男人都懂| 精品国产一区二区在线| 97久久超碰福利国产精品…| 69视频在线免费观看| 亚洲精品日产aⅴ| 懂色av一区二区三区| 欧美成人精品h版在线观看| 91精品久久久久久| 成人国产精品日本在线| 欧美限制级电影在线观看| 亚洲性av在线| 欧美激情国产日韩精品一区18| 国产成一区二区| 欧美国产中文字幕| 538国产精品一区二区在线| 日韩av在线网站| 国产精品99久久久久久久久| 红桃视频成人在线观看| 亚洲最大福利网| 国产综合久久久久久| 色一区av在线| 国产精品视频播放| 九九综合九九综合| 97免费视频在线播放| 亚洲国产精品免费| 国产精品观看在线亚洲人成网| 亚洲香蕉av在线一区二区三区| 国内精品久久久久久久| 久久99热精品| 久久五月天色综合| 欧美在线www| 国产精品27p| 亚洲精品av在线播放| 久久精品国产一区| 久久久免费观看| 国产综合久久久久| 中文字幕日韩电影| 精品久久在线播放| 91高清免费视频| 久久韩剧网电视剧| 九九热最新视频//这里只有精品| 黑人巨大精品欧美一区二区免费| 中文字幕少妇一区二区三区| 成人午夜激情免费视频| 亚洲精品白浆高清久久久久久| 性色av一区二区三区在线观看| 这里只有精品视频在线| 国产亚洲精品高潮| 亲爱的老师9免费观看全集电视剧| 精品久久香蕉国产线看观看亚洲| 欧美在线视频在线播放完整版免费观看| 日韩在线视频观看正片免费网站| 亚洲理论在线a中文字幕| 日本国产欧美一区二区三区|