本文转自:http://www.cnblogs.com/catcher1994/p/6021046.html

Web API,是一个能让前后端分离、解放前后端生产力的好东西。不过大部分公司应该都没能做到完全的前后端分离。API的实现方式有很

多,可以用ASP.NET Core、也可以用ASP.NET Web API、ASP.NET MVC、NancyFx等。说到Web API,不同的人有不同的做法,可能前台、

中台和后台各一个api站点,也有可能一个模块一个api站点,也有可能各个系统共用一个api站点,当然这和业务有必然的联系。

  安全顺其自然的成为Web API关注的重点之一。现在流行的OAuth 2.0是个很不错的东西,不过本文是暂时没有涉及到的,只是按照最最最

原始的思路做的一个授权验证。在之前的MVC中,我们可能是通过过滤器来处理这个身份的验证,在Core中,我自然就是选择Middleware来处

理这个验证。

  下面开始本文的正题:

  先编写一个能正常运行的api,不进行任何的权限过滤。

  1. 1 using Dapper;
  2. 2 using Microsoft.AspNetCore.Mvc;
  3. 3 using System.Data;
  4. 4 using System.Linq;
  5. 5 using System.Threading.Tasks;
  6. 6 using WebApi.CommandText;
  7. 7 using WebApi.Common;
  8. 8 using Common;
  9. 9
  10. 10 namespace WebApi.Controllers
  11. 11 {
  12. 12 [Route("api/[controller]")]
  13. 13 public class BookController : Controller
  14. 14 {
  15. 15
  16. 16 private DapperHelper _helper;
  17. 17 public BookController(DapperHelper helper)
  18. 18 {
  19. 19 this._helper = helper;
  20. 20 }
  21. 21
  22. 22 // GET: api/book
  23. 23 [HttpGet]
  24. 24 public async Task<IActionResult> Get()
  25. 25 {
  26. 26 var res = await _helper.QueryAsync(BookCommandText.GetBooks);
  27. 27 CommonResult<Book> json = new CommonResult<Book>
  28. 28 {
  29. 29 Code = "000",
  30. 30 Message = "ok",
  31. 31 Data = res
  32. 32 };
  33. 33 return Ok(json);
  34. 34 }
  35. 35
  36. 36 // GET api/book/5
  37. 37 [HttpGet("{id}")]
  38. 38 public IActionResult Get(int id)
  39. 39 {
  40. 40 DynamicParameters dp = new DynamicParameters();
  41. 41 dp.Add("@Id", id, DbType.Int32, ParameterDirection.Input);
  42. 42 var res = _helper.Query<Book>(BookCommandText.GetBookById, dp, null, true, null, CommandType.StoredProcedure).FirstOrDefault();
  43. 43 CommonResult<Book> json = new CommonResult<Book>
  44. 44 {
  45. 45 Code = "000",
  46. 46 Message = "ok",
  47. 47 Data = res
  48. 48 };
  49. 49 return Ok(json);
  50. 50 }
  51. 51
  52. 52 // POST api/book
  53. 53 [HttpPost]
  54. 54 public IActionResult Post([FromForm]PostForm form)
  55. 55 {
  56. 56 DynamicParameters dp = new DynamicParameters();
  57. 57 dp.Add("@Id", form.Id, DbType.Int32, ParameterDirection.Input);
  58. 58 var res = _helper.Query<Book>(BookCommandText.GetBookById, dp, null, true, null, CommandType.StoredProcedure).FirstOrDefault();
  59. 59 CommonResult<Book> json = new CommonResult<Book>
  60. 60 {
  61. 61 Code = "000",
  62. 62 Message = "ok",
  63. 63 Data = res
  64. 64 };
  65. 65 return Ok(json);
  66. 66 }
  67. 67
  68. 68 }
  69. 69
  70. 70 public class PostForm
  71. 71 {
  72. 72 public string Id { get; set; }
  73. 73 }
  74. 74
  75. 75 }
  api这边应该没什么好说的,都是一些常规的操作,会MVC的应该都可以懂。主要是根据id获取图书信息的方法(GET和POST)。这是我们后

面进行单元测试的两个主要方法。这样部署得到的一个API站点,是任何一个人都可以访问http://yourapidomain.com/api/book 来得到相关

的数据。现在我们要对这个api进行一定的处理,让只有权限的站点才能访问它。

  下面就是编写自定义的授权验证中间件了。

  Middleware这个东西大家应该都不会陌生了,OWIN出来的时候就有中间件这样的概念了,这里就不展开说明,在ASP.NET Core中是如何

实现这个中间件的可以参考官方文档 Middleware

  我们先定义一个我们要用到的option,ApiAuthorizedOptions

  1. 1 namespace WebApi.Middlewares
  2. 2 {
  3. 3 public class ApiAuthorizedOptions
  4. 4 {
  5. 5 //public string Name { get; set; }
  6. 6
  7. 7 public string EncryptKey { get; set; }
  8. 8
  9. 9 public int ExpiredSecond { get; set; }
  10. 10 }
  11. 11 }

  option内容比较简单,一个是EncryptKey ,用于对我们的请求参数进行签名,另一个是ExpiredSecond ,用于检验我们的请求是否超时。

与之对应的是在appsettings.json中设置的ApiKey节点

  1. 1 "ApiKey": {
  2. 2 //"username": "123",
  3. 3 //"password": "123",
  4. 4 "EncryptKey": "@*api#%^@",
  5. 5 "ExpiredSecond": "300"
  6. 6 }

  有了option,下面就可以编写middleware的内容了

  我们的api中就实现了get和post的方法,所以这里也就对get和post做了处理,其他http method,有需要的可以自己补充。

  这里的验证主要是下面的几个方面:

  1.参数是否被篡改

  2.请求是否已经过期

  3.请求的应用是否合法

  主检查方法:Check
  1. 1 /// <summary>
  2. 2 /// the main check method
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <param name="requestInfo"></param>
  6. 6 /// <returns></returns>
  7. 7 private async Task Check(HttpContext context, RequestInfo requestInfo)
  8. 8 {
  9. 9 string computeSinature = HMACMD5Helper.GetEncryptResult($"{requestInfo.ApplicationId}-{requestInfo.Timestamp}-{requestInfo.Nonce}", _options.EncryptKey);
  10. 10 double tmpTimestamp;
  11. 11 if (computeSinature.Equals(requestInfo.Sinature) &&
  12. 12 double.TryParse(requestInfo.Timestamp, out tmpTimestamp))
  13. 13 {
  14. 14 if (CheckExpiredTime(tmpTimestamp, _options.ExpiredSecond))
  15. 15 {
  16. 16 await ReturnTimeOut(context);
  17. 17 }
  18. 18 else
  19. 19 {
  20. 20 await CheckApplication(context, requestInfo.ApplicationId, requestInfo.ApplicationPassword);
  21. 21 }
  22. 22 }
  23. 23 else
  24. 24 {
  25. 25 await ReturnNoAuthorized(context);
  26. 26 }
  27. 27 }

  Check方法带了2个参数,一个是当前的httpcontext对象和请求的内容信息,当签名一致,并且时间戳能转化成double时才去校验是否超时

和Applicatioin的相关信息。这里的签名用了比较简单的HMACMD5加密,同样是可以换成SHA等加密来进行这一步的处理,加密的参数和规则是

随便定的,要有一个约定的过程,缺少灵活性(就像跟银行对接那样,银行说你就要这样传参数给我,不这样就不行,只好乖乖从命)。

  Check方法还用到了下面的4个处理

  1.子检查方法--超时判断CheckExpiredTime

  1. 1 /// <summary>
  2. 2 /// check the expired time
  3. 3 /// </summary>
  4. 4 /// <param name="timestamp"></param>
  5. 5 /// <param name="expiredSecond"></param>
  6. 6 /// <returns></returns>
  7. 7 private bool CheckExpiredTime(double timestamp, double expiredSecond)
  8. 8 {
  9. 9 double now_timestamp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
  10. 10 return (now_timestamp - timestamp) > expiredSecond;
  11. 11 }

  这里取了当前时间与1970年1月1日的间隔与请求参数中传过来的时间戳进行比较,是否超过我们在appsettings中设置的那个值,超过就是

超时了,没超过就可以继续下一个步骤。

  2.子检查方法--应用程序判断CheckApplication

  应用程序要验证什么呢?我们会给每个应用程序创建一个ID和一个访问api的密码,所以我们要验证这个应用程序的真实身份,是否是那些

有权限的应用程序。

  1. 1 /// <summary>
  2. 2 /// check the application
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <param name="applicationId"></param>
  6. 6 /// <param name="applicationPassword"></param>
  7. 7 /// <returns></returns>
  8. 8 private async Task CheckApplication(HttpContext context, string applicationId, string applicationPassword)
  9. 9 {
  10. 10 var application = GetAllApplications().Where(x => x.ApplicationId == applicationId).FirstOrDefault();
  11. 11 if (application != null)
  12. 12 {
  13. 13 if (application.ApplicationPassword != applicationPassword)
  14. 14 {
  15. 15 await ReturnNoAuthorized(context);
  16. 16 }
  17. 17 }
  18. 18 else
  19. 19 {
  20. 20 await ReturnNoAuthorized(context);
  21. 21 }
  22. 22 }

  先根据请求参数中的应用程序id去找到相应的应用程序,不能找到就说明不是合法的应用程序,能找到再去验证其密码是否正确,最后才确

定其能否取得api中的数据。

  下面两方法是处理没有授权和超时处理的实现:

  没有授权的返回方法ReturnNoAuthorized

  1. 1 /// <summary>
  2. 2 /// not authorized request
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <returns></returns>
  6. 6 private async Task ReturnNoAuthorized(HttpContext context)
  7. 7 {
  8. 8 BaseResponseResult response = new BaseResponseResult
  9. 9 {
  10. 10 Code = "401",
  11. 11 Message = "You are not authorized!"
  12. 12 };
  13. 13 context.Response.StatusCode = 401;
  14. 14 await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
  15. 15 }

  这里做的处理是将响应的状态码设置成401(Unauthorized)。

  超时的返回方法ReturnTimeOut

  1. 1 /// <summary>
  2. 2 /// timeout request
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <returns></returns>
  6. 6 private async Task ReturnTimeOut(HttpContext context)
  7. 7 {
  8. 8 BaseResponseResult response = new BaseResponseResult
  9. 9 {
  10. 10 Code = "408",
  11. 11 Message = "Time Out!"
  12. 12 };
  13. 13 context.Response.StatusCode = 408;
  14. 14 await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
  15. 15 }

  这里做的处理是将响应的状态码设置成408(Time Out)。

  下面就要处理Http的GET请求和POST请求了。

  HTTP GET请求的处理方法GetInvoke

  1. 1 /// <summary>
  2. 2 /// http get invoke
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <returns></returns>
  6. 6 private async Task GetInvoke(HttpContext context)
  7. 7 {
  8. 8 var queryStrings = context.Request.Query;
  9. 9 RequestInfo requestInfo = new RequestInfo
  10. 10 {
  11. 11 ApplicationId = queryStrings["applicationId"].ToString(),
  12. 12 ApplicationPassword = queryStrings["applicationPassword"].ToString(),
  13. 13 Timestamp = queryStrings["timestamp"].ToString(),
  14. 14 Nonce = queryStrings["nonce"].ToString(),
  15. 15 Sinature = queryStrings["signature"].ToString()
  16. 16 };
  17. 17 await Check(context, requestInfo);
  18. 18 }

  处理比较简单,将请求的参数赋值给RequestInfo,然后将当前的httpcontext和这个requestinfo交由我们的主检查方法Check去校验

这个请求的合法性。

  同理,HTTP POST请求的处理方法PostInvoke,也是同样的处理。

  1. 1 /// <summary>
  2. 2 /// http post invoke
  3. 3 /// </summary>
  4. 4 /// <param name="context"></param>
  5. 5 /// <returns></returns>
  6. 6 private async Task PostInvoke(HttpContext context)
  7. 7 {
  8. 8 var formCollection = context.Request.Form;
  9. 9 RequestInfo requestInfo = new RequestInfo
  10. 10 {
  11. 11 ApplicationId = formCollection["applicationId"].ToString(),
  12. 12 ApplicationPassword = formCollection["applicationPassword"].ToString(),
  13. 13 Timestamp = formCollection["timestamp"].ToString(),
  14. 14 Nonce = formCollection["nonce"].ToString(),
  15. 15 Sinature = formCollection["signature"].ToString()
  16. 16 };
  17. 17 await Check(context, requestInfo);
  18. 18 }

  最后是Middleware的构造函数和Invoke方法。

  1. 1   public ApiAuthorizedMiddleware(RequestDelegate next, IOptions<ApiAuthorizedOptions> options)
  2. 2 {
  3. 3 this._next = next;
  4. 4 this._options = options.Value;
  5. 5 }
  6. 6
  7. 7 public async Task Invoke(HttpContext context)
  8. 8 {
  9. 9 switch (context.Request.Method.ToUpper())
  10. 10 {
  11. 11 case "POST":
  12. 12 if (context.Request.HasFormContentType)
  13. 13 {
  14. 14 await PostInvoke(context);
  15. 15 }
  16. 16 else
  17. 17 {
  18. 18 await ReturnNoAuthorized(context);
  19. 19 }
  20. 20 break;
  21. 21 case "GET":
  22. 22 await GetInvoke(context);
  23. 23 break;
  24. 24 default:
  25. 25 await GetInvoke(context);
  26. 26 break;
  27. 27 }
  28. 28 await _next.Invoke(context);
  29. 29 }

  到这里,Middleware是已经编写好了,要在Startup中使用,还要添加一个拓展方法ApiAuthorizedExtensions

  1. 1 using Microsoft.AspNetCore.Builder;
  2. 2 using Microsoft.Extensions.Options;
  3. 3 using System;
  4. 4
  5. 5 namespace WebApi.Middlewares
  6. 6 {
  7. 7 public static class ApiAuthorizedExtensions
  8. 8 {
  9. 9 public static IApplicationBuilder UseApiAuthorized(this IApplicationBuilder builder)
  10. 10 {
  11. 11 if (builder == null)
  12. 12 {
  13. 13 throw new ArgumentNullException(nameof(builder));
  14. 14 }
  15. 15
  16. 16 return builder.UseMiddleware<ApiAuthorizedMiddleware>();
  17. 17 }
  18. 18
  19. 19 public static IApplicationBuilder UseApiAuthorized(this IApplicationBuilder builder, ApiAuthorizedOptions options)
  20. 20 {
  21. 21 if (builder == null)
  22. 22 {
  23. 23 throw new ArgumentNullException(nameof(builder));
  24. 24 }
  25. 25
  26. 26 if (options == null)
  27. 27 {
  28. 28 throw new ArgumentNullException(nameof(options));
  29. 29 }
  30. 30
  31. 31 return builder.UseMiddleware<ApiAuthorizedMiddleware>(Options.Create(options));
  32. 32 }
  33. 33 }
  34. 34 }

  到这里我们已经可以在Startup的Configure和ConfigureServices方法中配置这个中间件了

  这里还有一个不一定非要实现的拓展方法ApiAuthorizedServicesExtensions,但我个人还是倾向于实现这个ServicesExtensions。

  1. 1 using Microsoft.Extensions.DependencyInjection;
  2. 2 using System;
  3. 3
  4. 4 namespace WebApi.Middlewares
  5. 5 {
  6. 6 public static class ApiAuthorizedServicesExtensions
  7. 7 {
  8. 8
  9. 9 /// <summary>
  10. 10 /// Add response compression services.
  11. 11 /// </summary>
  12. 12 /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
  13. 13 /// <returns></returns>
  14. 14 public static IServiceCollection AddApiAuthorized(this IServiceCollection services)
  15. 15 {
  16. 16 if (services == null)
  17. 17 {
  18. 18 throw new ArgumentNullException(nameof(services));
  19. 19 }
  20. 20
  21. 21 return services;
  22. 22 }
  23. 23
  24. 24 /// <summary>
  25. 25 /// Add response compression services and configure the related options.
  26. 26 /// </summary>
  27. 27 /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
  28. 28 /// <param name="configureOptions">A delegate to configure the <see cref="ResponseCompressionOptions"/>.</param>
  29. 29 /// <returns></returns>
  30. 30 public static IServiceCollection AddApiAuthorized(this IServiceCollection services, Action<ApiAuthorizedOptions> configureOptions)
  31. 31 {
  32. 32 if (services == null)
  33. 33 {
  34. 34 throw new ArgumentNullException(nameof(services));
  35. 35 }
  36. 36 if (configureOptions == null)
  37. 37 {
  38. 38 throw new ArgumentNullException(nameof(configureOptions));
  39. 39 }
  40. 40
  41. 41 services.Configure(configureOptions);
  42. 42 return services;
  43. 43 }
  44. 44 }
  45. 45 }

ApiAuthorizedServicesExtensions

  为什么要实现这个拓展方法呢?个人认为

  Options、Middleware、Extensions、ServicesExtensions这四个是实现一个中间件的标配(除去简单到不行的那些中间件)

  Options给我们的中间件提供了一些可选的处理,提高了中间件的灵活性;

  Middleware是我们中间件最最重要的实现;

  Extensions是我们要在Startup的Configure去表明我们要使用这个中间件;

  ServicesExtensions是我们要在Startup的ConfigureServices去表明我们把这个中间件添加到容器中。

  下面是完整的Startup

  1. 1 using Microsoft.AspNetCore.Builder;
  2. 2 using Microsoft.AspNetCore.Hosting;
  3. 3 using Microsoft.Extensions.Configuration;
  4. 4 using Microsoft.Extensions.DependencyInjection;
  5. 5 using Microsoft.Extensions.Logging;
  6. 6 using System;
  7. 7 using WebApi.Common;
  8. 8 using WebApi.Middlewares;
  9. 9
  10. 10 namespace WebApi
  11. 11 {
  12. 12 public class Startup
  13. 13 {
  14. 14 public Startup(IHostingEnvironment env)
  15. 15 {
  16. 16 var builder = new ConfigurationBuilder()
  17. 17 .SetBasePath(env.ContentRootPath)
  18. 18 .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  19. 19 .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
  20. 20
  21. 21 if (env.IsEnvironment("Development"))
  22. 22 {
  23. 23 // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
  24. 24 builder.AddApplicationInsightsSettings(developerMode: true);
  25. 25 }
  26. 26
  27. 27 builder.AddEnvironmentVariables();
  28. 28 Configuration = builder.Build();
  29. 29 }
  30. 30
  31. 31 public IConfigurationRoot Configuration { get; }
  32. 32
  33. 33 // This method gets called by the runtime. Use this method to add services to the container
  34. 34 public void ConfigureServices(IServiceCollection services)
  35. 35 {
  36. 36 // Add framework services.
  37. 37 services.AddApplicationInsightsTelemetry(Configuration);
  38. 38 services.Configure<IISOptions>(options =>
  39. 39 {
  40. 40
  41. 41 });
  42. 42
  43. 43 services.Configure<DapperOptions>(options =>
  44. 44 {
  45. 45 options.ConnectionString = Configuration.GetConnectionString("DapperConnection");
  46. 46 });
  47. 47
  48. 48 //api authorized middleware
  49. 49 services.AddApiAuthorized(options =>
  50. 50 {
  51. 51 options.EncryptKey = Configuration.GetSection("ApiKey")["EncryptKey"];
  52. 52 options.ExpiredSecond = Convert.ToInt32(Configuration.GetSection("ApiKey")["ExpiredSecond"]);
  53. 53 });
  54. 54
  55. 55
  56. 56 services.AddMvc();
  57. 57
  58. 58 services.AddSingleton<DapperHelper>();
  59. 59 }
  60. 60
  61. 61 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
  62. 62 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  63. 63 {
  64. 64
  65. 65 loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  66. 66 loggerFactory.AddDebug();
  67. 67
  68. 68 app.UseDapper();
  69. 69
  70. 70 //api authorized middleware
  71. 71 app.UseApiAuthorized();
  72. 72
  73. 73 app.UseApplicationInsightsRequestTelemetry();
  74. 74
  75. 75 app.UseApplicationInsightsExceptionTelemetry();
  76. 76
  77. 77 app.UseMvc();
  78. 78 }
  79. 79 }
  80. 80 }

  万事具备,只欠测试!!

  建个类库项目,写个单元测试看看。

  1. 1 using Common;
  2. 2 using Newtonsoft.Json;
  3. 3 using System;
  4. 4 using System.Collections.Generic;
  5. 5 using System.Net.Http;
  6. 6 using System.Threading.Tasks;
  7. 7 using Xunit;
  8. 8
  9. 9 namespace WebApiTest
  10. 10 {
  11. 11 public class BookApiTest
  12. 12 {
  13. 13 private HttpClient _client;
  14. 14 private string applicationId = "1";
  15. 15 private string applicationPassword = "123";
  16. 16 private string timestamp = (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds.ToString();
  17. 17 private string nonce = new Random().Next(1000, 9999).ToString();
  18. 18 private string signature = string.Empty;
  19. 19
  20. 20 public BookApiTest()
  21. 21 {
  22. 22 _client = new HttpClient();
  23. 23 _client.BaseAddress = new Uri("http://localhost:8091/");
  24. 24 _client.DefaultRequestHeaders.Clear();
  25. 25 signature = HMACMD5Helper.GetEncryptResult($"{applicationId}-{timestamp}-{nonce}", "@*api#%^@");
  26. 26 }
  27. 27
  28. 28 [Fact]
  29. 29 public async Task book_api_get_by_id_should_success()
  30. 30 {
  31. 31 string queryString = $"applicationId={applicationId}&timestamp={timestamp}&nonce={nonce}&signature={signature}&applicationPassword={applicationPassword}";
  32. 32
  33. 33 HttpResponseMessage message = await _client.GetAsync($"api/book/4939?{queryString}");
  34. 34 var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);
  35. 35
  36. 36 Assert.Equal("000", result.Code);
  37. 37 Assert.Equal(4939, result.Data.Id);
  38. 38 Assert.True(message.IsSuccessStatusCode);
  39. 39 }
  40. 40
  41. 41 [Fact]
  42. 42 public async Task book_api_get_by_id_should_failure()
  43. 43 {
  44. 44 string inValidSignature = Guid.NewGuid().ToString();
  45. 45 string queryString = $"applicationId={applicationId}&timestamp={timestamp}&nonce={nonce}&signature={inValidSignature}&applicationPassword={applicationPassword}";
  46. 46
  47. 47 HttpResponseMessage message = await _client.GetAsync($"api/book/4939?{queryString}");
  48. 48 var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);
  49. 49
  50. 50 Assert.Equal("401", result.Code);
  51. 51 Assert.Equal(System.Net.HttpStatusCode.Unauthorized, message.StatusCode);
  52. 52 }
  53. 53
  54. 54 [Fact]
  55. 55 public async Task book_api_post_by_id_should_success()
  56. 56 {
  57. 57 var data = new Dictionary<string, string>();
  58. 58 data.Add("applicationId", applicationId);
  59. 59 data.Add("applicationPassword", applicationPassword);
  60. 60 data.Add("timestamp", timestamp);
  61. 61 data.Add("nonce", nonce);
  62. 62 data.Add("signature", signature);
  63. 63 data.Add("Id", "4939");
  64. 64 HttpContent ct = new FormUrlEncodedContent(data);
  65. 65
  66. 66 HttpResponseMessage message = await _client.PostAsync("api/book", ct);
  67. 67 var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);
  68. 68
  69. 69 Assert.Equal("000", result.Code);
  70. 70 Assert.Equal(4939, result.Data.Id);
  71. 71 Assert.True(message.IsSuccessStatusCode);
  72. 72
  73. 73 }
  74. 74
  75. 75 [Fact]
  76. 76 public async Task book_api_post_by_id_should_failure()
  77. 77 {
  78. 78 string inValidSignature = Guid.NewGuid().ToString();
  79. 79 var data = new Dictionary<string, string>();
  80. 80 data.Add("applicationId", applicationId);
  81. 81 data.Add("applicationPassword", applicationPassword);
  82. 82 data.Add("timestamp", timestamp);
  83. 83 data.Add("nonce", nonce);
  84. 84 data.Add("signature", inValidSignature);
  85. 85 data.Add("Id", "4939");
  86. 86 HttpContent ct = new FormUrlEncodedContent(data);
  87. 87
  88. 88 HttpResponseMessage message = await _client.PostAsync("api/book", ct);
  89. 89 var result = JsonConvert.DeserializeObject<CommonResult<Book>>(message.Content.ReadAsStringAsync().Result);
  90. 90
  91. 91 Assert.Equal("401", result.Code);
  92. 92 Assert.Equal(System.Net.HttpStatusCode.Unauthorized, message.StatusCode);
  93. 93 }
  94. 94 }
  95. 95 }

  测试用的是XUnit。这里写了get和post的测试用例。

  下面来看看测试的效果。

  测试通过。这里是直接用VS自带的测试窗口来运行测试,比较直观。

  当然也可以通过我们的dotnet test命令来运行测试。

  本文的Demo已经上传到Github:

  https://github.com/hwqdt/Demos/tree/master/src/ASPNETCoreAPIAuthorizedDemo

  Thanks for your reading!

如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!    
声明: 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果您发现博客中出现了错误,或者有更好的建议、想法,请及时与我联系!!如果想找我私下交流,可以私信或者加我QQ。
 

[转]用Middleware给ASP.NET Core Web API添加自己的授权验证的更多相关文章

  1. 用Middleware给ASP.NET Core Web API添加自己的授权验证

    Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做到完全的前后端分离.API的实现方式有很 多,可以用ASP.NET Core.也可以用ASP.NET Web ...

  2. 使用JWT创建安全的ASP.NET Core Web API

    在本文中,你将学习如何在ASP.NET Core Web API中使用JWT身份验证.我将在编写代码时逐步简化.我们将构建两个终结点,一个用于客户登录,另一个用于获取客户订单.这些api将连接到在本地 ...

  3. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  4. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  5. ASP.NET Core Web API 最佳实践指南

    原文地址: ASP.NET-Core-Web-API-Best-Practices-Guide 介绍 当我们编写一个项目的时候,我们的主要目标是使它能如期运行,并尽可能地满足所有用户需求. 但是,你难 ...

  6. 如何在ASP.NET Core Web API中使用Mini Profiler

    原文如何在ASP.NET Core Web API中使用Mini Profiler 由Anuraj发表于2019年11月25日星期一阅读时间:1分钟 ASPNETCoreMiniProfiler 这篇 ...

  7. 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)

    对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...

  8. 在ASP.NET Core Web API上使用Swagger提供API文档

    我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的API文档功能.当设置IISExpress的默认启动路由到Swagger的API文档页 ...

  9. Docker容器环境下ASP.NET Core Web API应用程序的调试

    本文主要介绍通过Visual Studio 2015 Tools for Docker – Preview插件,在Docker容器环境下,对ASP.NET Core Web API应用程序进行调试.在 ...

随机推荐

  1. .NET 程序集单元测试工具 SmokeTest 应用指南

    Smoke Test(冒烟测试),也称Regression Test(回归测试),是对软件的安装和基本功能的测试.一般地我们使用脚本来实现Smoke Test的自动化,可借用虚拟机的snapshot机 ...

  2. MongoDB安装与故障

    下载完毕后   bin为官方代码   data为自行创建的文件夹 db存在数据 log存在日志   启动MongoDB 通过cmd到db的文件目录 通过mongod.exe代码执行data下的log文 ...

  3. socket编程为什么需要htons(), ntohl(), ntohs(),htons() 函数

    在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题.这是就可能用到htons(), ntohl(), ntohs(),htons()这4个函数. 网络字节顺序与本地字节顺序之间的转 ...

  4. Php基础知识测试题

    一:选择题 1. LAMP具体结构不包含下面哪种(A      ) A:Windows系统               如果是这个就是WMP B:Apache服务器 C:MySQL数据库 D:PHP语 ...

  5. Bootstrap人民币玩家攻略

    用bootstrap及其它基于它的框架,做了多次网站大改版~对bootstrap的特点有了越来越深的了解~从一开始接触时觉得超级鸡肋,到后来觉得方便,再到后来觉得还是能不用就别用了~为什么这么说?我们 ...

  6. H5实现本地预览图片

    我们使用H5可以很容易的实现图片上传前对其进行预览的功能 Html代码如下: <!DOCTYPE html> <html lang="en"> <he ...

  7. Nginx反向代理部署指南

    一.反向代理 我们都知道,80端口是web服务的默认端口,其他主机访问web服务器也是默认和80端口进行web交互,而一台服务器也只有一个80端口,这是约定俗成的标准. 我们来看下面两个场景: 1.服 ...

  8. sqlserver 游标的使用

    declare @temp_temp uniqueidentifier--临时变量 DECLARE aaa CURSOR for select Id from A ------------------ ...

  9. JDBC——Java代码与数据库链接的桥梁

    常用数据库的驱动程序及JDBC URL: Oracle数据库: 驱动程序包名:ojdbc14.jar 驱动类的名字:oracle.jdbc.driver.OracleDriver JDBC URL:j ...

  10. Xshell显示中文乱码问题

    [文件]–>[打开]–>在打开的session中选择连接的那个,点击[属性] -> [终端], 编码选择为:Unicode(UTF-8),然后重新连接服务器即可.也可以在Xshell ...