• 1、Swagger管理API说明文档
  • 2、JwtBearer token验证
  • 3、Swagger UI增加Authentication
  • 4、EntityFrameworkCore+MySQL
  • 5、在.net core 3.1下使用Log4net

元旦过后就没什么工作上的任务了,这当然不能让领导看在眼里,动手实践一下新技术吧。于是准备搭一个webapi的中间件框架。

由于自己的云主机是台linux服务器,双核2G的centos+1M 没有数据盘,也用不起RDS,如果装个Windows Server那么肯定卡的不行,所以一直想尝试一下跨平台的感觉。

由于这篇随笔不是定位于教程,故基础知识一概略过。

项目中使用到的包清单

  1. <ItemGroup>
  2. <PackageReference Include="IdentityModel" Version="4.1.1" />
  3. <PackageReference Include="log4net" Version="2.0.8" />
  4. <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.1" />
  5. <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.1" />
  6. <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
  7. <PrivateAssets>all</PrivateAssets>
  8. <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  9. </PackageReference>
  10. <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
  11. <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
  12. <PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.19" />
  13. <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.0" />
  14. <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.0.0" />
  15. <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0" />
  16. <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0" />
  17. </ItemGroup>

 

1)Startup

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using IdentityModel;
  9. using Microsoft.AspNetCore.Authentication.JwtBearer;
  10. using Microsoft.AspNetCore.Builder;
  11. using Microsoft.AspNetCore.Hosting;
  12. using Microsoft.AspNetCore.Mvc;
  13. using Microsoft.EntityFrameworkCore;
  14. using Microsoft.Extensions.Configuration;
  15. using Microsoft.Extensions.DependencyInjection;
  16. using Microsoft.Extensions.Hosting;
  17. using Microsoft.Extensions.Logging;
  18. using Microsoft.IdentityModel.Tokens;
  19. using Microsoft.OpenApi.Models;
  20. using MySql.Data.EntityFrameworkCore.Extensions;
  21. using Swashbuckle.AspNetCore.Swagger;
  22. using tokendemo.Models;
  23. namespace tokendemo
  24. {
  25. public class Startup
  26. {
  27. public Startup(IConfiguration configuration)
  28. {
  29. Configuration = configuration;
  30. }
  31. public IConfiguration Configuration { get; }
  32. // This method gets called by the runtime. Use this method to add services to the container.
  33. public void ConfigureServices(IServiceCollection services)
  34. {
  35. services.AddControllers();
  36. services.Configure<TokenManagement>(Configuration.GetSection("tokenManagement"));
  37. var token = Configuration.GetSection("tokenManagement").Get<TokenManagement>();
  38. services.AddAuthentication(x =>
  39. {
  40. x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  41. x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  42. }).AddJwtBearer(x =>
  43. {
  44. x.RequireHttpsMetadata = false;
  45. x.SaveToken = true;
  46. x.TokenValidationParameters = new TokenValidationParameters
  47. {
  48. ValidateIssuerSigningKey = true,
  49. IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
  50. ValidIssuer = token.Issuer,
  51. ValidAudience = token.Audience,
  52. ValidateIssuer = false,
  53. ValidateAudience = false
  54. };
  55. });
  56. services.AddSwaggerGen(c =>
  57. {
  58. c.SwaggerDoc("v1",
  59. new OpenApiInfo
  60. {
  61. Title = "XXX项目接口文档",
  62. Version = "v1",
  63. Contact = new OpenApiContact
  64. {
  65. Email = "xyf_xiao@cquni.com",
  66. Name = "肖远峰",
  67. Url = new Uri("http://datacool.cnblogs.com")
  68. }
  69. });
  70. // 为 Swagger 设置xml文档注释路径
  71. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
  72. var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
  73. c.IncludeXmlComments(xmlPath);
  74. c.AddSecurityDefinition("Bearer",
  75. new OpenApiSecurityScheme
  76. {
  77. Description = "请输入OAuth接口返回的Token,前置Bearer。示例:Bearer {Roken}",
  78. Name = "Authorization",
  79. In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
  80. Type = SecuritySchemeType.ApiKey
  81. });
  82. c.AddSecurityRequirement(new OpenApiSecurityRequirement
  83. {
  84. {
  85. new OpenApiSecurityScheme
  86. {
  87. Reference = new OpenApiReference()
  88. {
  89. Id = "Bearer",
  90. Type = ReferenceType.SecurityScheme
  91. }
  92. }, Array.Empty<string>()
  93. }
  94. });
  95. });
  96. var posdbConnString = Configuration.GetConnectionString("POS_Db");
  97. services.AddDbContext<posdbContext>(option =>
  98. {
  99. option.UseMySql(posdbConnString, null);
  100. });
  101. services.AddScoped<IAuthenticateService, TokenAuthenticationService>();
  102. services.AddScoped<IUserService, UserService>();
  103. }
  104. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  105. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  106. {
  107. if (env.IsDevelopment())
  108. {
  109. app.UseDeveloperExceptionPage();
  110. }
  111. app.UseHttpsRedirection();
  112. app.UseAuthentication();
  113. app.UseRouting();
  114. app.UseAuthorization();
  115. app.UseEndpoints(endpoints =>
  116. {
  117. endpoints.MapControllers();
  118. });
  119. app.UseSwagger();
  120. //启用中间件服务生成SwaggerUI,指定Swagger JSON终结点
  121. app.UseSwaggerUI(c =>
  122. {
  123. c.SwaggerEndpoint("/swagger/v1/swagger.json", "XXX接口文档 V1");
  124. c.RoutePrefix = string.Empty;//设置根节点访问
  125. });
  126. app.UseLog4net();
  127. }
  128. }
  129. }
  1. using Microsoft.AspNetCore.Builder;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Threading.Tasks;
  8. namespace tokendemo
  9. {
  10. public static class LoggeServiceExt
  11. {
  12. /// 使用log4net配置
  13. /// <param name="app"></param>
  14. /// <returns></returns>
  15. public static IApplicationBuilder UseLog4net(this IApplicationBuilder app)
  16. {
  17. var logRepository = log4net.LogManager.CreateRepository(Assembly.GetEntryAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy));
  18. log4net.Config.XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
  19. return app;
  20. }
  21. }
  22. }
  1. public interface IUserService
  2. {
  3. bool IsValid(LoginRequestDTO req);
  4. }
  5. public interface IAuthenticateService
  6. {
  7. bool IsAuthenticated(LoginRequestDTO request, out string token);
  8. }
  9. public class UserService : IUserService
  10. {
  11. public bool IsValid(LoginRequestDTO req)
  12. {
  13. return true;
  14. }
  15. }
  16. public class TokenAuthenticationService : IAuthenticateService
  17. {
  18. private readonly IUserService _userService;
  19. private readonly TokenManagement _tokenManagement;
  20. private readonly posdbContext db;
  21. public TokenAuthenticationService(IUserService userService, IOptions<TokenManagement> tokenManagement, posdbContext posdb)
  22. {
  23. _userService = userService;
  24. _tokenManagement = tokenManagement.Value;
  25. db = posdb;
  26. }
  27. public string GetAuthentUser()
  28. {
  29. return JsonConvert.SerializeObject(db.SysApiAuthorize.ToList());
  30. }
  31. public bool IsAuthenticated(LoginRequestDTO request, out string token)
  32. {
  33. token = string.Empty;
  34. if (!_userService.IsValid(request))
  35. return false;
  36. var claims = new[]
  37. {
  38. new Claim(ClaimTypes.Name,request.Username)
  39. };
  40. var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
  41. var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
  42. var jwtToken = new JwtSecurityToken(_tokenManagement.Issuer, _tokenManagement.Audience, claims, expires: DateTime.Now.AddMinutes(_tokenManagement.AccessExpiration), signingCredentials: credentials);
  43. token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
  44. return true;
  45. }
  46. }

 

token验证是我关注的重点,而Swagger支持查看文档的同时调用API,也支持授权认证,所以水到渠成。代码命名都是比较规范的,当然大部分来源于别人的文章,这里就不作过多说明了。

asp.net core对依赖注入思想是贯彻始终的,新人需要在这个思想的领悟上下苦功夫才能驾驭她。

2)配置文件

  1. 1 {
  2. 2 "Logging": {
  3. 3 "LogLevel": {
  4. 4 "Default": "Information",
  5. 5 "Microsoft": "Warning",
  6. 6 "Microsoft.Hosting.Lifetime": "Information"
  7. 7 }
  8. 8 },
  9. 9 "AllowedHosts": "*",
  10. 10 "tokenManagement": {
  11. 11 "secret": "157300523770123456",
  12. 12 "issuer": "datacool.cnblogs.com",
  13. 13 "audience": "WebApi",
  14. 14 "accessExpiration": 30,
  15. 15 "refreshExpiration": 60
  16. 16 },
  17. 17 "ConnectionStrings": {
  18. 18 "POS_Db": "server=localhost;userid=root;pwd=dba#2020;port=3306;database=posdb;sslmode=none"
  19. 19 }
  20. 20 }

appsettings

  1. 1 <?xml version="1.0" encoding="utf-8" ?>
  2. 2 <log4net>
  3. 3 <!--定义输出到文件中-->
  4. 4 <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
  5. 5 <!--定义文件存放位置-->
  6. 6 <param name="AppendToFile" value="true"/>
  7. 7 <!--最小锁定模型以允许多个进程可以写入同一个文件-->
  8. 8 <param name="LockingModel" value="log4net.Appender.FileAppender.MinimalLock"/>
  9. 9 <param name="StaticLogFileName" value="true"/>
  10. 10 <!--保存路径-->
  11. 11 <param name="File" value="Log/Logs_"/>
  12. 12 <param name="DatePattern" value="yyyyMMdd&quot;.txt&quot;"/>
  13. 13 <param name="StaticLogFileName" value="false"/>
  14. 14 <param name="RollingStyle" value="Date"/>
  15. 15 <layout type="log4net.Layout.PatternLayout">
  16. 16 <!--每条日志末尾的文字说明-->
  17. 17 <footer value=""/>
  18. 18 <!--输出格式-->
  19. 19 <!--样例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info-->
  20. 20 <conversionPattern value="%newline%date 级别:%-5level 日志描述:%message%newline"/>
  21. 21 </layout>
  22. 22 </appender>
  23. 23 <root>
  24. 24 <!--文件形式记录日志-->
  25. 25 <appender-ref ref="LogFileAppender"/>
  26. 26 </root>
  27. 27 </log4net>

log4net.config

Scaffold-DbContext “server=localhost;userid=root;pwd=dba#2020;port=3306;database=posdb;sslmode=none;” Pomelo.EntityFrameworkCore.MySql -OutputDir Models -Force

由于我的数据库是先存在了,所以直接使用了nutget控制台生成了数据库上下文对象和实体。注意向导生成的数据库上下文里是把数据库连接字符串写死的,需要修改。本例是写入appsettings.json里的。请重点看一下上面的配置和Startup里获取配置的代码。

3)关联代码,几个数据传输类

  1. public class TokenManagement
  2. {
  3. [JsonProperty("secret")]
  4. public string Secret { get; set; }
  5. [JsonProperty("issuer")]
  6. public string Issuer { get; set; }
  7. [JsonProperty("audience")]
  8. public string Audience { get; set; }
  9. [JsonProperty("accessExpiration")]
  10. public int AccessExpiration { get; set; }
  11. [JsonProperty("refreshExpiration")]
  12. public int RefreshExpiration { get; set; }
  13. }
  1. public class WeatherForecast
  2. {
  3. public DateTime Date { get; set; }
  4. public int TemperatureC { get; set; }
  5. public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
  6. public string Summary { get; set; }
  7. }
  1. public class LoginRequestDTO
  2. {
  3. [Required]
  4. [JsonProperty("username")]
  5. public string Username { get; set; }
  6. [Required]
  7. [JsonProperty("password")]
  8. public string Password { get; set; }
  9. }

3)API控制器

  1. 1 using System;
  2. 2 using System.Collections.Generic;
  3. 3 using System.Linq;
  4. 4 using System.Threading.Tasks;
  5. 5 using log4net;
  6. 6 using Microsoft.AspNetCore.Authorization;
  7. 7 using Microsoft.AspNetCore.Mvc;
  8. 8
  9. 9 namespace tokendemo.Controllers
  10. 10 {
  11. 11 [ApiController]
  12. 12 [Route("[controller]")]
  13. 13 [Authorize]
  14. 14 public class WeatherForecastController : ControllerBase
  15. 15 {
  16. 16 private readonly ILog _logger;
  17. 17 public WeatherForecastController()
  18. 18 {
  19. 19 _logger = LogManager.GetLogger(typeof(WeatherForecastController));
  20. 20 }
  21. 21 private static readonly string[] Summaries = new[]
  22. 22 {
  23. 23 "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
  24. 24 };
  25. 25
  26. 26
  27. 27 [HttpGet]
  28. 28 public IEnumerable<WeatherForecast> Get()
  29. 29 {
  30. 30 var rng = new Random();
  31. 31 _logger.Info("OK");
  32. 32 return Enumerable.Range(1, 5).Select(index => new WeatherForecast
  33. 33 {
  34. 34 Date = DateTime.Now.AddDays(index),
  35. 35 TemperatureC = rng.Next(-20, 55),
  36. 36 Summary = Summaries[rng.Next(Summaries.Length)]
  37. 37 })
  38. 38 .ToArray();
  39. 39 }
  40. 40
  41. 41 }
  42. 42 }

这个大家应该很熟悉了,这就是vs2019向导创建的API控制器。[Authorize]标记会导致401错误,就是表示先要去获取access token,在Header里带入Bearer+空格+token才可以正常调用。

授权控制器

 

 

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Authorization;
  6. using Microsoft.AspNetCore.Http;
  7. using Microsoft.AspNetCore.Mvc;
  8. namespace tokendemo.Controllers
  9. {
  10. [Route("api/[controller]")]
  11. [ApiController]
  12. public class AuthenticationController : ControllerBase
  13. {
  14. private readonly IAuthenticateService _authService;
  15. public AuthenticationController(IAuthenticateService service)
  16. {
  17. _authService = service;
  18. }
  19. [AllowAnonymous]
  20. [HttpPost, Route("requestToken")]
  21. public ActionResult RequestToken([FromBody] LoginRequestDTO request)
  22. {
  23. if (!ModelState.IsValid)
  24. {
  25. return BadRequest("Invalid Request");
  26. }
  27. string token;
  28. var authTime = DateTime.UtcNow;
  29. if (_authService.IsAuthenticated(request, out token))
  30. {
  31. return Ok(new
  32. {
  33. access_token = token,
  34. token_type = "Bearer",
  35. profile = new
  36. {
  37. sid = request.Username,
  38. auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds()
  39. }
  40. });
  41. }
  42. return BadRequest("Invalid Request");
  43. }
  44. }
  45. }
  • 1、妥妥的吃了次螃蟹,收获了经验
  • 2、正在“为自己挖一口井”的路上
  • 3、.net core算是入门了
  • 4、源码我是没自信放到github的,后面会加上下载链接
  • 5、伙计们分享起来吧,这个生态建设任重而道远啊。

这里是源码下载的地址:https://files.cnblogs.com/files/datacool/tokendemo.zip

 

版权声明:本文为datacool原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/datacool/p/datacool_dotnetcore_demo.html