.NET core webApi 使用JWT验证签名

  • A+
所属分类:.NET技术
摘要

1.跨语言使用。2.服务器端无需再保存任何东西,只需要客户端保存token就可以。

 

一、为什么使用JWT

1.跨语言使用。

2.服务器端无需再保存任何东西,只需要客户端保存token就可以。

3.实现简单。

4.统一认证方式,如果是移动端也要验证的话,jwt也支持就无需修改,否则客户端 服务器一套,移动端 服务器又是一套

当然缺陷也是很明显,就是退出登录后,已发放的token无法销毁,可以继续访问。就是令牌给你了,如果别人盗取了你的令牌,我也是认的,我只认令牌不认人。也可以设置令牌有效期,假如设置过期有效时间为10分钟,就算你拿到了令牌想用也已经过期了,但是这就要求客户端每次想要做什么,先去申请令牌,然后在去操作,这就很麻烦。内部系统的话是可以用这种模式的,如果对外的不建议使用。对外的可以使用传统的签名认证方法。

二、在.net core webApi 搭建jwt并且使用

第一步:首先要在程序中读取appSettings配置文件信息

创建一个AppSettings.cs类,存放读取配置信息的函数 代码如下,使用Nuget  安装Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json,Microsoft.Extensions.Configuration.Binder

using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; using System; using System.Collections.Generic; using System.Linq;  namespace WebApi.Core.Common {     public class AppSettings     {         static IConfiguration Configuration { get; set; }         static string contentPath { get; set; }          public AppSettings(string contentPath)         {             string Path = "appsettings.json";              //如果你把配置文件 是 根据环境变量来分开了,可以这样写             //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";              Configuration = new ConfigurationBuilder()                .SetBasePath(contentPath)                .Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性                .Build();         }          public AppSettings(IConfiguration configuration)         {             Configuration = configuration;         }          /// <summary>         /// 封装要操作的字符         /// </summary>         /// <param name="sections">节点配置</param>         /// <returns></returns>         public static string app(params string[] sections)         {             try             {                  if (sections.Any())                 {                     return Configuration[string.Join(":", sections)];                 }             }             catch (Exception)             {              }              return "";         }          /// <summary>         /// 递归获取配置信息数组         /// </summary>         /// <typeparam name="T"></typeparam>         /// <param name="sections"></param>         /// <returns></returns>         public static List<T> app<T>(params string[] sections)         {             List<T> list = new List<T>();             Configuration.Bind(string.Join(":", sections), list);             return list;         }     } }

找到webApi项目,打开Startup类,在ConfigureService函数 注册AppSettings

.NET core webApi 使用JWT验证签名

 

 

编辑appsettings.json,新增红色框子的内容。

.NET core webApi 使用JWT验证签名

 

 

获取调试一下

            //注册appsettings读取类             services.AddSingleton(new Appsettings(Configuration));             var text = Appsettings.app(new string[] { "AppSettings", "ConnectionStringSql" });             Console.WriteLine($"ConnectionString:{text}");             Console.ReadLine();    

运行后的结果

.NET core webApi 使用JWT验证签名

 

 

接下来,我们开始正式的在项目中,注册和使用JWT,首先配置一下jwt需要的参数,在appsettings.json中,SecretKey必须大于16个,是大于,不是大于等于

.NET core webApi 使用JWT验证签名

 

 

在Api项目中 添加Nuget 包 IdentityModel,Microsoft.AspNetCore.Authentication.JwtBearer,Microsoft.AspNetCore.Authorization

.NET core webApi 使用JWT验证签名

 

 

在model项目中添加一个tokenModel

using System; using System.Collections.Generic; using System.Text;  namespace WebApi.Core.Model {     /// <summary>     /// 令牌     /// </summary>     public class TokenModel     {         /// <summary>         /// Id         /// </summary>         public string Uid { get; set; }         /// <summary>         /// 角色         /// </summary>         public string Role { get; set; }      } }

在Api项目中新建一个文件夹 Authorization,新建一个JwtHelper  里面有生成token 和解析token 两个方法

using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using WebApi.Core.Common; using WebApi.Core.Model;  namespace WebApi.Core.Api.Authorization {     public class JwtHelper     {         /// <summary>         /// 获取token信息         /// </summary>         /// <param name="tokenModel"></param>         /// <returns></returns>         public static string issueJwt(TokenModel tokenModel)         {             //获取Appsetting配置信息             string iss = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });             string aud = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" });             string secret = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });              var claims = new List<Claim>                 {                  /*                  * 特别重要:                    1、这里将用户的部分信息,比如 uid 存到了Claim 中,如果你想知道如何在其他地方将这个 uid从 Token 中取出来,
              请看下边的SerializeJwt() 方法,或者在整个解决方案,搜索这个方法,看哪里使用了!                    2、你也可以研究下 HttpContext.User.Claims ,具体的你可以看看 Policys/PermissionHandler.cs 类中是如何使用的。                  */

//nbf 生效时间 、Jti 编号、iat 签发时间、aud 受众、exp 过期时间

                 new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),                 new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),                 new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,                 //这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间                 new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"),                 new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()),                 new Claim(JwtRegisteredClaimNames.Iss,iss),                 new Claim(JwtRegisteredClaimNames.Aud,aud),                };              // 可以将一个用户的多个角色全部赋予;             claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));              //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)             var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));             var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);              var jwt = new JwtSecurityToken(                 issuer: iss,                 claims: claims,                 signingCredentials: creds);              var jwtHandler = new JwtSecurityTokenHandler();             var encodedJwt = jwtHandler.WriteToken(jwt);              return encodedJwt;         }          /// <summary>         /// 解析         /// </summary>         /// <param name="jwtStr"></param>         /// <returns></returns>         public static TokenModel SerializeJwt(string jwtStr)         {             var jwtHandler = new JwtSecurityTokenHandler();             JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);             object role;             try             {                 jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);             }             catch (Exception e)             {                 Console.WriteLine(e);                 throw;             }             var tm = new TokenModel             {                 Uid = jwtToken.Id.ToString(),                 Role = role != null ? role.ToString() : "",             };             return tm;         }     } }

在UserController 新建一个login接口,获取token

        /// <summary>         /// 登录验证并且获取token         /// </summary>         /// <param name="loginModel"></param>         /// <returns></returns>         [HttpPost]         public IActionResult LoginValidate(LoginModel loginModel)         {             string jwtStr = string.Empty;             bool suc = false;              if (loginModel != null)             {                 //加登录验证                 if (loginModel.UserName == "admin" && loginModel.PassWord == "123456")                 {                     TokenModel tokenModel = new TokenModel { Uid = loginModel.UserName, Role = loginModel.Role };                     jwtStr = JwtHelper.issueJwt(tokenModel);                     suc = true;                 }             }              return Ok(new {                  success=suc,                 token = jwtStr             });         }

按F5启动,可以看到token已经生成,客户端也已经获取到。

.NET core webApi 使用JWT验证签名

 

 

 

获取到了token我们怎么使用呢? 下一步 我们要在Swagger中开启 JWT服务,先安装包 Swashbuckle.AspNetCore.Filters

然后找到SwaggerSetUp.cs的AddSwaggerSetUp方法中增加以下代码

 public static void AddSwaggerSetup(this IServiceCollection services)         {             if (services == null) throw new ArgumentNullException(nameof(services));              var ApiName = "Webapi.Core";              services.AddSwaggerGen(c =>             {                 c.SwaggerDoc("V1", new OpenApiInfo                 {                     // {ApiName} 定义成全局变量,方便修改                     Version = "V1",                     Title = $"{ApiName} 接口文档——Netcore 3.0",                     Description = $"{ApiName} HTTP API V1",                  });                 c.OrderActionsBy(o => o.RelativePath);                  // 获取xml注释文件的目录                 var xmlPath = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Api.xml");                 c.IncludeXmlComments(xmlPath, true);//默认的第二个参数是false,这个是controller的注释,记得修改                  // 获取xml注释文件的目录                 var xmlPathModel = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Model.xml");                 c.IncludeXmlComments(xmlPathModel, true);//默认的第二个参数是false,这个是controller的注释,记得修改                  //在 header中添加token,传递到后台                 c.OperationFilter<SecurityRequirementsOperationFilter>();                  #region Token绑定到configureServices                  c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme                 {                     Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)"",                     Name = "Authorization",//jwt默认的参数名称                     In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)                     Type = SecuritySchemeType.ApiKey                 });                 #endregion              });          }

按F5运行 可以看到 token入口了,按要求的格式在 value中输入 token 以后的请求head 就会一直加入token了。

.NET core webApi 使用JWT验证签名

 

.NET core webApi 使用JWT验证签名.NET core webApi 使用JWT验证签名

 

 

下面该授权token 的认证了,在SetupService 文件夹下 新建一个AuthJwtSetup.cs 类 如下

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; using WebApi.Core.Common;  namespace WebApi.Core.Api.SetUpService {     public static class AuthJwtSetup     {         public static void AddAuthorizationJwtSetUp(this IServiceCollection services)         {             if (services == null) throw new ArgumentNullException(nameof(services));              //读取配置文件             var symmetricKeyAsBase64 = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" });             var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);             var signingKey = new SymmetricSecurityKey(keyByteArray);             var Issuer = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" });             var Audience = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" });                // 令牌验证参数             var tokenValidationParameters = new TokenValidationParameters             {                 ValidateIssuerSigningKey = true,                 IssuerSigningKey = signingKey,                 ValidateIssuer = true,                 ValidIssuer = Issuer,//发行人                 ValidateAudience = true,                 ValidAudience = Audience,//订阅人                 ValidateLifetime = true,                 ClockSkew = TimeSpan.FromSeconds(30), //token过期后 还可以继续多访问30秒                 RequireExpirationTime = true,             };              //2.1【认证】、core自带官方JWT认证             // 开启Bearer认证             services.AddAuthentication("Bearer")              // 添加JwtBearer服务              .AddJwtBearer(o =>              {                  o.TokenValidationParameters = tokenValidationParameters;                  o.Events = new JwtBearerEvents                  {                      OnAuthenticationFailed = context =>                      {                          // 如果过期,则把<是否过期>添加到,返回头信息中                          if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))                          {                              context.Response.Headers.Add("Token-Expired", "true");                          }                          return Task.CompletedTask;                      }                  };              });         }     } }

startup.cs的ConfigureServices 方法添加 服务注入

//jwt授权验证 services.AddAuthorizationSetup();

 

Configure方法添加 下面的代码

.NET core webApi 使用JWT验证签名

 

接口授权策略验证,在UserController里面添加一个方法 

.NET core webApi 使用JWT验证签名

 

按F5启动调试,先获取Admin角色权限的token,添加token到header中,然后请求 验证角色权限那个接口,验证成功。

.NET core webApi 使用JWT验证签名

 

.NET core webApi 使用JWT验证签名

 

.NET core webApi 使用JWT验证签名

 

如果获取的token 不是Admin 角色权限我们再来试一下,看看是什么结果,结果就是没有访问权限。

.NET core webApi 使用JWT验证签名

 

.NET core webApi 使用JWT验证签名

 

下面该解析Token 了,试一下,添加一个接口,步骤同上,先获取token,然后绑定token,最后调用一下解析接口,看结果我们已经解析成功了。

.NET core webApi 使用JWT验证签名

 

.NET core webApi 使用JWT验证签名