zoukankan      html  css  js  c++  java
  • [翻译]创建ASP.NET WebApi RESTful 服务(8)

    本章讨论创建安全的WebApi服务,到目前为止,我们实现的API都是基于未加密的HTTP协议,大家都知道在Web中传递身份信息必须通过HTTPS,接下来我们来实现这一过程。

    使用HTTPS

    其实可以通过IIS配置,将整个WebApi的访问都配置为Https,但实际上,如果希望只是对部分方法进行认证,那就必须通过认证身份信息进行处理。

    下面介绍通过Filter来实现这一过程,如果身份认证不通过,就返回一条信息,提示访问者通过https进行访问。

       1:  public class ForceHttpsAttribute : AuthorizationFilterAttribute
       2:      {
       3:          public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
       4:          {
       5:              var request = actionContext.Request;
       6:   
       7:              if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
       8:              {
       9:                  var html = "<p>Https is required</p>";
      10:   
      11:                  if (request.Method.Method == "GET")
      12:                  {
      13:                      actionContext.Response = request.CreateResponse(HttpStatusCode.Found);
      14:                      actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
      15:   
      16:                      UriBuilder httpsNewUri = new UriBuilder(request.RequestUri);
      17:                      httpsNewUri.Scheme = Uri.UriSchemeHttps;
      18:                      httpsNewUri.Port = 443;
      19:   
      20:                      actionContext.Response.Headers.Location = httpsNewUri.Uri;
      21:                  }
      22:                  else
      23:                  {
      24:                      actionContext.Response = request.CreateResponse(HttpStatusCode.NotFound);
      25:                      actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
      26:                  }
      27:   
      28:              }
      29:          }
      30:      }

    通过actionContext参数获取Request和Response对象,对URI进行检查,如果不是以HTTPS开头,就返回443代码。

    使用的方法有两种,一种是在WebAPIConfig中注册为全局的Attribute。

       1:    public static void Register(HttpConfiguration config)
       2:      {
       3:          config.Filters.Add(new ForceHttpsAttribute());
       4:      }

    另一种是对制定的类或者方法进行拦截。

       1:  //Enforce HTTPS on the entire controller
       2:      [Learning.Web.Filters.ForceHttps()]
       3:      public class CoursesController : BaseApiController
       4:      {
       5:          //Enforce HTTPS on POST method only
       6:          [Learning.Web.Filters.ForceHttps()]
       7:          public HttpResponseMessage Post([FromBody] CourseModel courseModel)
       8:          {
       9:   
      10:          }
      11:      }

    通过Basic Authentication进行认证

    当前所有的API都是Public的,网络上的任意用户都可以请求资源。实际的项目中肯定要对访问者进行必要的限制。

    • 假设对于客户端的请求“http://{your_port}/api/students/{userName}”,当有正确的身份信息时,我们返回username为 “TaiseerJoudeh”的信息,否则,返回错误;
    • 如果请求是POST类型,如“http://{your_port}/api/courses/2/students/{userName}”,那么修改者也必须通过身份认证才可以进行修改。

    什么是Basic Authentication

    Basic Authentication提供了一种在Http Request被处理之前先行进行身份认证的模式,它在防止Dos等方面具有重要作用。Basic Authentication要求在请求时必须在Http Header提供基于Base64编码的用户名和密码信息。这种认证一般应该通过HTTPS来实现。

       1:   public class LearningAuthorizeAttribute : AuthorizationFilterAttribute
       2:      {
       3:   
       4:          [Inject]
       5:          public LearningRepository TheRepository { get; set; }
       6:   
       7:          public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
       8:          {
       9:              //Case that user is authenticated using forms authentication
      10:              //so no need to check header for basic authentication.
      11:              if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
      12:              {
      13:                  return;
      14:              }
      15:   
      16:              var authHeader = actionContext.Request.Headers.Authorization;
      17:   
      18:              if (authHeader != null)
      19:              {
      20:                  if (authHeader.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) &&
      21:                  !String.IsNullOrWhiteSpace(authHeader.Parameter))
      22:                  {
      23:                      var credArray = GetCredentials(authHeader);
      24:                      var userName = credArray[0];
      25:                      var password = credArray[1];
      26:   
      27:                      if (IsResourceOwner(userName, actionContext))
      28:                      {
      29:                          //You can use Websecurity or asp.net memebrship provider to login, for
      30:                          //for he sake of keeping example simple, we used out own login functionality
      31:                          if (TheRepository.LoginStudent(userName, password))
      32:                          {
      33:                              var currentPrincipal = new GenericPrincipal(new GenericIdentity(userName), null);
      34:                              Thread.CurrentPrincipal = currentPrincipal;
      35:                              return;
      36:                          }
      37:                      }
      38:                  }
      39:              }
      40:   
      41:              HandleUnauthorizedRequest(actionContext);
      42:          }
      43:   
      44:          private string[] GetCredentials(System.Net.Http.Headers.AuthenticationHeaderValue authHeader)
      45:          {
      46:   
      47:              //Base 64 encoded string
      48:              var rawCred = authHeader.Parameter;
      49:              var encoding = Encoding.GetEncoding("iso-8859-1");
      50:              var cred = encoding.GetString(Convert.FromBase64String(rawCred));
      51:   
      52:              var credArray = cred.Split(':');
      53:   
      54:              return credArray;
      55:          }
      56:   
      57:          private bool IsResourceOwner(string userName, System.Web.Http.Controllers.HttpActionContext actionContext)
      58:          {
      59:              var routeData = actionContext.Request.GetRouteData();
      60:              var resourceUserName = routeData.Values["userName"] as string;
      61:   
      62:              if (resourceUserName == userName)
      63:              {
      64:                  return true;
      65:              }
      66:              return false;
      67:          }
      68:   
      69:          private void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
      70:          {
      71:              actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
      72:   
      73:              actionContext.Response.Headers.Add("WWW-Authenticate",
      74:              "Basic Scheme='eLearning' location='http://localhost:8323/account/login'");
      75:   
      76:          }
      77:      }

    上述代码实现了以下逻辑:

    1. 从Request Header中获取认证信息;
    2. 确认Header的Schema被设置为basic,并包含了正确的Base64编码字符串;
    3. 将字符串转换为“username:password”格式,获取各自内容;
    4. 验证身份信息,确定是否具有相应权限;
    5. 如果Credentials验证无误,设置当前线程的Identity信息,以便子请求能够重用;
    6. 如果验证有无,则返回401错误。

    现在可以对指定的方法进行标记。

       1:      public class StudentsController : BaseApiController
       2:      {
       3:          [LearningAuthorizeAttribute]
       4:          public HttpResponseMessage Get(string userName)
       5:          {
       6:   
       7:          }
       8:      }
       9:   
      10:      public class EnrollmentsController : BaseApiController
      11:      {
      12:          [LearningAuthorizeAttribute]
      13:          public HttpResponseMessage Post(int courseId, [FromUri]string userName, [FromBody]Enrollment enrollment)
      14:          {
      15:   
      16:          }
      17:      }
    现在分别用FireFox和Fiddler进行测试。假设路径如下:http://localhost:{your_port}/api/students/TaiseerJoudeh
    • FireFox:返回401,因为没有身份信息,一个弹出框会弹出,要求输入用户名和密码。输入正确的用户名和密码,就可以看到返回的json信息。而接下来的子请求,则无需再进行认证;
    • Fiddler:需要先拼接一个Base64加密“username:password”字符串,可以通过这里实现。注意:这不能算是加密,真正的加密必须通过HTTPS实现。当用户名密码正确时,会返回200和正确的json信息。

     

    来源:http://bitoftech.net/2013/12/03/enforce-https-asp-net-web-api-basic-authentication/

  • 相关阅读:
    初识增长黑客Growth Hacker
    OpenCV学习目录(持续更新)
    leetcode算法-回文判断
    leetcode算法-整数反转
    日常撸题技巧【总结】
    hihocoder 题目类型整理
    【hiho】92-93--素数【数论】
    notepad打造成C/C++IDE【Notepad】
    ACM知识点总结【杂】
    Nastya Studies Informatics
  • 原文地址:https://www.cnblogs.com/tukzer/p/3662521.html
Copyright © 2011-2022 走看看