《Asp.Net Core3 + Vue3入坑教程》 此教程适合新手入门或者先后端分离尝试者。能够根据图文一步一步进操做编码也能够选择直接查看源码。每一篇文章都有对应的源码html
本文将 .Net Core 3升级成 .Net 5前端
Asp.Net Core后端项目vue
Vue3 前端项目git
暂未发表敬请期待...github
本文为《Asp.Net Core3 + Vue3入坑教程》系列教程的后端第五篇 - .Net Core 3升级成 .Net 5 & JWT。上文已经为Simple项目增长了EF Core与Postgresql数据库的链接,本文继续为Simple项目增长JWT(JSON Web Token)的应用,目标是让除了用户请求认证接口以外的其他请求都须要带着JWT!在使用以前先将SKD的版本升级成 .Net 5。sql
JWT详解参考 https://jwt.io/introduction数据库
https://dotnet.microsoft.com/download/visual-studio-sdks?utm_source=getdotnetsdk&utm_medium=referraljson
代码以下:后端
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Simple_Asp.Net_Core.ServiceProvider { public static class JWT { public static void AddJWT(this IServiceCollection services) { services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.RequireHttpsMetadata = false; o.Events = new JwtBearerEvents() { OnAuthenticationFailed = context => { //Token expired if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) context.Response.Headers.Add("Token-Expired", "true"); return Task.CompletedTask; }, }; o.TokenValidationParameters = new TokenValidationParameters { // 是否验证失效时间 ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), ValidateAudience = true, // 这里采用动态验证的方式,在从新登录时,刷新token,旧token就强制失效了 AudienceValidator = AudienceValidator, // 是否验证Issuer ValidateIssuer = false, // 是否验证SecurityKey ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Const.SecurityKey)) }; }); } private static bool AudienceValidator(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) { return Audiences.IsNewestAudience(audiences.FirstOrDefault()); } } }
代码以下:api
using System; using System.Collections.Generic; namespace Simple_Asp.Net_Core.ServiceProvider { public static class Audiences { private static IDictionary<string, string> _audiences = new Dictionary<string, string>(); public static string UpdateAudience(string userNo) { if (string.IsNullOrWhiteSpace(userNo)) return string.Empty; var audience = $"{userNo}_{DateTime.Now}"; _audiences[userNo] = audience; return audience; } public static bool IsNewestAudience(string audience) { if (string.IsNullOrWhiteSpace(audience)) return false; var userName = audience.Split('_')[0]; if (!_audiences.ContainsKey(userName)) return false; else return _audiences[userName] == audience; } } }
namespace Simple_Asp.Net_Core.ServiceProvider { internal class Const { public const string Domain = "http://localhost:81"; public const string SecurityKey = "Simple_Asp.Net_Core"; } }
代码以下:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json.Serialization; using Simple_Asp.Net_Core.Data; using Simple_Asp.Net_Core.ServiceProvider; using System; namespace Simple_Asp.Net_Core { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddJWT(); services.AddDbContext<CommanderContext>(options => options.UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=123456")); services.AddCORS(); services.AddMvc(); services.AddSwagger(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddScoped<ICommanderRepo, SqlCommanderRepo>(); services.AddControllers().AddNewtonsoftJson(s => { s.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); }); } app.UseCors("CorsTest"); app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); } } }
HTTP401状态详解 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/401
当前模拟用户信息获取与用户信息校验用户校验
Action增长[AllowAnonymous]特性,让此接口能够接收任何的请求
代码以下:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using Simple_Asp.Net_Core.Dtos; using Simple_Asp.Net_Core.Extensions; using Simple_Asp.Net_Core.ServiceProvider; using System; using System.IdentityModel.Tokens.Jwt; using System.Text; namespace Simple_Asp.Net_Core.Controllers { [Route("api/[controller]")] [ApiController] public class OAuthController : ControllerBase { [HttpPost] [AllowAnonymous] public IActionResult Authenticate(string name, string password) { // 此处需补充用户校验与用户具体信息获取... var user = new UserProviderDto(name, password); var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(Const.SecurityKey); var tokenDescriptor = new SecurityTokenDescriptor { Audience = Audiences.UpdateAudience(user.Name), Subject = user.GetClaimsIdentity(), Expires = DateTime.UtcNow.AddDays(0.5), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return Ok(new { access_token = tokenString }); } } }
代码以下:
namespace Simple_Asp.Net_Core.Dtos { public class UserProviderDto { public UserProviderDto(string name, string password) { Name = name; Password = password; } public string ID { get; set; } /// <summary> /// 用户名 /// </summary> public string Name { get; set; } /// <summary> /// 手机号 /// </summary> public string Phone { get; set; } /// <summary> /// 电子邮箱 /// </summary> public string Mail { get; set; } public string Password { get; set; } } }
代码以下:
using Newtonsoft.Json; using Simple_Asp.Net_Core.Dtos; using System.Collections.Generic; using System.Linq; using System.Security.Claims; namespace Simple_Asp.Net_Core.Extensions { public static class ClaimLoginUserExtensions { private const string USER = "User"; public static ClaimsIdentity GetClaimsIdentity(this UserProviderDto user) { return new ClaimsIdentity(new Claim[] { new Claim(USER, JsonConvert.SerializeObject(user)) }); } public static UserProviderDto GetLoginUser(this IEnumerable<Claim> claims) { var user = JsonConvert.DeserializeObject<UserProviderDto>(claims.Get(USER)); return user; } public static string Get(this IEnumerable<Claim> claims, string claimType) { return claims.Where(v => v.Type == claimType).First().Value; } } }
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyIjoie1wiSURcIjpudWxsLFwiTmFtZVwiOlwiYWRtaW5cIixcIlBob25lXCI6bnVsbCxcIk1haWxcIjpudWxsLFwiUGFzc3dvcmRcIjpcIjEyMzQ1NlwifSIsIm5iZiI6MTYxNDIzOTAwNSwiZXhwIjoxNjE0MjgyMjA1LCJpYXQiOjE2MTQyMzkwMDUsImF1ZCI6ImFkbWluXzIwMjEvMi8yNSDmmJ_mnJ_lm5sgMTU6NDM6MjUifQ.yrjK8qX45mNOQ3taecIc-QVaBDlN4QUOdBPRExvpejk"
实例以下:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyIjoie1wiSURcIjpudWxsLFwiTmFtZVwiOlwiYWRtaW5cIixcIlBob25lXCI6bnVsbCxcIk1haWxcIjpudWxsLFwiUGFzc3dvcmRcIjpcIjEyMzQ1NlwifSIsIm5iZiI6MTYxNDIzOTAwNSwiZXhwIjoxNjE0MjgyMjA1LCJpYXQiOjE2MTQyMzkwMDUsImF1ZCI6ImFkbWluXzIwMjEvMi8yNSDmmJ_mnJ_lm5sgMTU6NDM6MjUifQ.yrjK8qX45mNOQ3taecIc-QVaBDlN4QUOdBPRExvpejk
能够利用JWT在线解析工具(https://jwt.io/#debugger-io) 将前面获取的JWT的信息解析出来
本文为Simple项目增长JWT(JSON Web Token)的应用,除了用户请求认证接口以外的其他请求都须要带着JWT,本文还简单的实现了用户单一登入,扩展类JWT.cs的AudienceValidator方法会判断用户使用的Token是不是最新的,若是用户重复登入则旧的Token会失效!目前只是简单的使用了静态变量来保存Token的信息,能够结合具体状况将Token保存至Redis或者数据库中。
JWT的更多配置能够参考官方API资料 https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.jwtbearer?view=aspnetcore-5.0
注意:源码调试过程当中若是出现xml文件路径错误,须要参照第一章(后端项目搭建与Swagger配置步骤)Swagger配置“配置XML 文档文件”步骤,取消勾选而后再选中 ,将XML路径设置成与你的电脑路径匹配!
博客JWT(推荐学习) https://www.cnblogs.com/7tiny/archive/2019/06/13/11012035.html
jwt官方资料 https://jwt.io