找学校的网站,抖音小程序开放平台,wordpress 密码查看,西安房产网58原文地址#xff1a;http://www.cnblogs.com/landeanfen/p/5287064.html 阅读目录 一、为什么需要身份认证二、Basic基础认证的原理解析 1、常见的认证方式2、Basic基础认证原理三、Basic基础认证的代码示例 1、登录过程2、/Home/Index主界面3、WebApiCORS验证部分#xff08… 原文地址http://www.cnblogs.com/landeanfen/p/5287064.html 阅读目录 一、为什么需要身份认证二、Basic基础认证的原理解析 1、常见的认证方式2、Basic基础认证原理三、Basic基础认证的代码示例 1、登录过程2、/Home/Index主界面3、WebApiCORS验证部分重点四、优化 1、解决API的问题2、解决ajax的问题3、解决特殊不想使用验证的方法五、总结 正文 前言最近讨论到数据库安全的问题于是就引出了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认证也不例外主要原理就是加密用户信息生成票据每次请求的时候将票据带过来验证。这样说可能有点抽象我们详细分解每个步骤 首先登陆的时候验证用户名、密码如果登陆成功则将用户名、密码按照一定的规则生成加密的票据信息Ticket将票据信息返回到前端。如果登陆成功前端会收到票据信息然后跳转到主界面并且将票据信息也带到主界面的ActionResult里面(例如跳转的url可以这样写/Home/Index?TicketTicket)在主界面的ActionResult里面通过参数得到票据信息Ticket然后将Ticket信息保存到ViewBag里面传到前端。在主界面的前端发送Ajax请求的时候将票据信息加入到请求的Head里面将票据信息随着请求一起发送到服务端去。在WebApi服务里面定义一个类继承AuthorizeAttribute类然后重写父类的OnAuthorization方法在OnAuthorization方法里面取到当前http请求的Head从Head里面取到我们前端传过来的票据信息。解密票据信息从解密的信息里面得到用户名和密码然后验证用户名和密码是否正确。如果正确表示验证通过否则返回未验证的请求401。 这个基本的原理。下面就按照这个原理来看看每一步的代码如何实现。 回到顶部 三、Basic基础认证的代码示例 首先说下我们的示例场景上次介绍 CORS 的时候我们在一个解决方案里面放了两个项目Web和WebApiCORS我们这次还是以这个为例来说明。 回到顶部 1、登录过程 1.1、Web前端 bodydiv styletext-align:center; div用户名input typetext idtxt_username //div div密 码input typepassword idtxt_password //div divinput typebutton value登录 idbtn_login classbtn-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 namestrUser/param /// param namestrPwd/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。如何开启WebApi里面的Session请参考http://www.cnblogs.com/tinya/p/4563641.html 正如上面的原理部分说的登录如果失败则直接返回如果成功则将生成的票据Ticket带到前端传到主界面/Home/Index下面我们就来看看主界面Home/Index。 回到顶部 2、/Home/Index主界面 public class HomeController : Controller{// GET: Homepublic ActionResult Index(string UserName, string Ticket) { ViewBag.UserName UserName; ViewBag.Ticket Ticket; return View(); } } html
head meta nameviewport contentwidthdevice-width / titleIndex/title script src~/Content/jquery-1.9.1.js/script link href~/Content/bootstrap/css/bootstrap.css relstylesheet / script src~/Content/bootstrap/js/bootstrap.js/script script src~/Scripts/Home/Index.js/script script typetext/javascript //打开页面的时候保存票据信息 var UserName ViewBag.UserName; var Ticket ViewBag.Ticket; /script /head body div当前登录用户ViewBag.UserName/div div iddiv_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.GetCustomAttributesAllowAnonymousAttribute().OfTypeAllowAnonymousAttribute(); 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 nameid参数Id/param /// returns返回数据/returns [HttpGet] public string GetAllChargingData(string id) { return ChargingData id; } } 增加了特性标注之后每次请求这个API里面的接口之前程序会先进入到我们override过的 OnAuthorization() 方法里面验证通过之后才会进到相应的方法里面去执行否则返回401。 回到顶部 四、优化 通过上面的几步基本就能达到我们想要的身份认证的效果但是总是感觉不太方便主要不太方便的点有以下几个。 每次新建一个API对应的接口上面都要标注 [RequestAuthorize] 这个一个东西感觉好麻烦。每次发送ajax请求都要在beforeSend事件里面加 XHR.setRequestHeader(Authorization, BasicAuth Ticket); 这个感觉也麻烦。如果有些WebApi服务的某些方法我们不想使用这个验证让它可以匿名用户验证比如我们的登录方法Login。该怎么处理呢。关于以上两点我们优化下 回到顶部 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 nameid参数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里面写了。 回到顶部 3、解决特殊不想使用验证的方法 如果我们某些方法不想使用验证使得它可以让匿名用户访问我们可以在方法的上面加特性标注 [AllowAnonymous] 申明该方法运行匿名访问。比如 public class ChargingController : BaseApiController{/// summary/// 得到所有数据 /// /summary /// returns返回数据/returns [HttpGet] public string GetAllChargingData() { return Success; } /// summary /// 得到当前Id的所有数据 /// /summary /// param nameid参数Id/param /// returns返回数据/returns [HttpGet] [AllowAnonymous] public string GetAllChargingData(string id) { return ChargingData id; } } 转载于:https://www.cnblogs.com/hhhh2010/p/8011705.html