zoukankan      html  css  js  c++  java
  • 《Asp.Net Core3 + Vue3入坑教程》

    简介

    《Asp.Net Core3 + Vue3入坑教程》 此教程适合新手入门或者前后端分离尝试者。可以根据图文一步一步进操作编码也可以选择直接查看源码。每一篇文章都有对应的源码

    本文将 .Net Core 3升级成 .Net 5

    目录

    《Asp.Net Core3 + Vue3入坑教程》系列教程目录

    Asp.Net Core后端项目

    1. 后端项目搭建与Swagger配置步骤
    2. 配置CROS策略解决跨域问题
    3. AutoMapper & Restful API & DI
    4. EF Core & Postgresql
    5. (本文).Net Core 3升级成 .Net 5 & JWT
    6. (暂未发表敬请期待...)

    Vue3 前端项目

    暂未发表敬请期待...

    本文简介

    本文为《Asp.Net Core3 + Vue3入坑教程》系列教程的后端第五篇 - .Net Core 3升级成 .Net 5 & JWT。上文已经为Simple项目增加了EF Core与Postgresql数据库的连接,本文继续为Simple项目增加JWT(JSON Web Token)的应用,目标是让除了用户请求认证接口之外的其余请求都需要带着JWT!在使用之前先将SKD的版本升级成 .Net 5。

    JWT详解参考 https://jwt.io/introduction

    把Simple项目从 .Net Core 3升级成 .Net 5 & 使用JWT

    .Net Core 3升级成 .Net 5

    上官网下载 .Net 5 SDK

    https://dotnet.microsoft.com/download/visual-studio-sdks?utm_source=getdotnetsdk&utm_medium=referral

    确保VS版本支持 .net 5 SDK(如果是2019的话,升级成最新的即可)

    打开项目,右键解决方案,打开项目属性

    将目标框架调整成 .NET 5.0

    更新Nuget包

    项目应用JWT

    在ServiceProvider文件夹下新建JWT扩展类

    代码如下:

    
    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());
            }
        }
    }
    
    

    在ServiceProvider文件夹下新建新建类Audiences.cs和Const.cs

    代码如下:

    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";
        }
    }
    

    在类中直接引入nuget包,或者使用Nuget包管理工具引入Microsoft.AspNetCore.Authentication.JwtBearer

    Startup.cs 配置调整,增加认证与授权配置

    代码如下:

    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());
            }
        }
    }
    
    

    接下来就是应用了

    首先我们在 CommandsController 控制器上增加特性 [Authorize]

    然后启动项目,可以发现,swagger上发出的请求出现了401错误

    401的错误就是没有权限

    HTTP401状态详解 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/401

    现在需要为swagger的请求增加Token

    首先增加JWT获取接口,新建控制器OAuthController.cs

    当前模拟用户信息获取与用户信息校验用户校验

    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
                });
            }
        }
    }
    
    

    在Dto文件夹下增加用户Dto UserProviderDto.cs

    代码如下:

    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; }
        }
    }
    
    

    新建 Extensions 文件夹 、 新建ClaimLoginUserExtensions 扩展类

    代码如下:

    
    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;
            }
        }
    }
    
    

    启动项目,调用api/OAuth接口获取Token

    得到Token

     "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyIjoie1wiSURcIjpudWxsLFwiTmFtZVwiOlwiYWRtaW5cIixcIlBob25lXCI6bnVsbCxcIk1haWxcIjpudWxsLFwiUGFzc3dvcmRcIjpcIjEyMzQ1NlwifSIsIm5iZiI6MTYxNDIzOTAwNSwiZXhwIjoxNjE0MjgyMjA1LCJpYXQiOjE2MTQyMzkwMDUsImF1ZCI6ImFkbWluXzIwMjEvMi8yNSDmmJ_mnJ_lm5sgMTU6NDM6MjUifQ.yrjK8qX45mNOQ3taecIc-QVaBDlN4QUOdBPRExvpejk"
    

    将Token赋到我们的请求上,在swagger上可以直接配置,点击 Authorize 按钮

    将Token附上,注意开头为Bearer (Bearer后面接着一个空格!):

    实例如下:

    Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyIjoie1wiSURcIjpudWxsLFwiTmFtZVwiOlwiYWRtaW5cIixcIlBob25lXCI6bnVsbCxcIk1haWxcIjpudWxsLFwiUGFzc3dvcmRcIjpcIjEyMzQ1NlwifSIsIm5iZiI6MTYxNDIzOTAwNSwiZXhwIjoxNjE0MjgyMjA1LCJpYXQiOjE2MTQyMzkwMDUsImF1ZCI6ImFkbWluXzIwMjEvMi8yNSDmmJ_mnJ_lm5sgMTU6NDM6MjUifQ.yrjK8qX45mNOQ3taecIc-QVaBDlN4QUOdBPRExvpejk
    

    再点击 Authorize 按钮

    调用/api/Commands请求 - 请求成功

    代码编写与配置已经全部完成

    可以利用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

    GitHub源码

    注意:源码调试过程中如果出现xml文件路径错误,需要参照第一章(后端项目搭建与Swagger配置步骤)Swagger配置“配置XML 文档文件”步骤,取消勾选然后再选中 ,将XML路径设置成与你的电脑路径匹配!

    https://github.com/Impartsoft/Simple_Asp.Net_Core/tree/master/Simple_Asp.Net_Core 5.SDK Update %26 JWT

    参考资料

    博客JWT(推荐学习) https://www.cnblogs.com/7tiny/archive/2019/06/13/11012035.html

    官方资料 https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.jwtbearer?view=aspnetcore-5.0

    jwt官方资料 https://jwt.io

    欢迎大家批评指正,共同学习,共同进步!
    作者:Iannnnnnnnnnnnn
    出处:https://www.cnblogs.com/Iannnnnnnnnnnnn
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Alice and Bob 要用到辗转相减
    Java经典设计模式
    设计模式---创建类---建造者模式
    luogu4267 TamingtheHerd (dp)
    nowcoder172C 保护 (倍增lca+dfs序+主席树)
    nowcoder172A 中位数 (二分答案)
    bzoj4985 评分 (二分答案+dp)
    luogu4269 Snow Boots G (并查集)
    luogu4268 Directory Traversal (dfs)
    bzoj1001/luogu4001 狼抓兔子 (最小割/平面图最小割转对偶图最短路)
  • 原文地址:https://www.cnblogs.com/Iannnnnnnnnnnnn/p/14444048.html
Copyright © 2011-2022 走看看