NETCore中使用JWT

NETCore中使用JWT

什么是JWT?

​ JSON Web Token(简称JWT),是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。

JWT常用于哪些场景?

​ 授权:适用于单点登录。例:当用户登录成功时,服务器会返回一个token给当前登录用户,且用户登录后访问系统的各个模块都应携带该token进行请求,当token过期时,则不允许用户访问系统模块。

​ 信息交换:jwt是各端(客户端、服务器端)之间安全地传输信息的好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),发送方与接收方可通过约定好的加密秘钥进行数据的解析。

JWT令牌结构

​ JWT以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:header(头部)、payload(有效载荷)和signature(签名),即结构为header.payload.signature。

1、header(头部)

​ 头部是令牌的第一部分,通常由两部分组成:令牌的类型(即JWT)和令牌所使用的签名算法,如SHA256、HMAC等。header示例如下:

{
  "alg": "HS256",
  "typ": "JWT"
}

​ 通过Base64对上述JSON编码后的结果为eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9,并作为JWT的第一部分。

2、payload(有效载荷)

​ 有效载荷是令牌的第二部分,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。主要有以下三种类型: registered(注册的), public(公开的)和 private claims(私有的声明)。

​ (1)注册声明(非强制性声明):主要包含iss(jwt发布者)、sub(面向的用户)、aud(接收方)、exp(过期时间)、iat(jwt签发时间)、jti(jwt身份标识)、nbf(在某个时间点前的token不可用)

​ (2)公开的声明:使用JWT的人员可以随意定义上述声明。

​ (3)私有声明:提供方和接收方共同定义的声明。

​ payload的示例如下:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

​ 通过Base64对上述JSON编码后的结果为eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ,并作为JWT的第二部分。

3、signature(签名)

​ 签名是令牌的第三部分,由header和payload进行64位编码后再使用加密算法加密即可,示例如下:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret);//secret为自定义的密码,如进行SHA256加密后的密码

​ 下面为完整的JWT示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

​ 可在JWT官网(https://jwt.io/#debugger-io)中进行数据的解析,如下图是解析的结果:

NETCore中使用JWT_第1张图片

JWT的使用

1、NETCore控制台中使用JWT

​ 具体代码如下:

using JWT.Algorithms;
using JWT.Builder;
using JWT.Exceptions;
using JWT.Serializers;
using System;
using System.Collections.Generic;

namespace JWT.Api
{
    class Program
    {
        //将"JWT"三个字母通过SHA256加密后得到
        private const string secret = "fc93cb07e1ad92898527100e58a1cf1d1e7f65e9a266a6f87f3c84feb541c7b3";

        static void Main(string[] args)
        {
            JWTEncode();//获取JWT  方式一
            JWTEncode_2(); //获取JWT  方式二
            Console.ReadKey();
        }
        /// 
        /// 获取JWT  方式一
        /// 
        public static void JWTEncode()
        {
            ///组成jwt的header
            //var header = new Dictionary
            //{
            //    { "alg","HS256"},
            //    { "typ", "JWT" },
            //};
            //定义payload中的数据  里面的数据可随意填,一般都是返回用户数据
            var payload = new Dictionary
            {
                { "name", "张三" },
                { "time", DateTime.Now }
            };
            //加密的秘钥,这个接收端也需要有相同的  “jwt”字符串进行SH256加密
           
            //生成JWT签名的算法
            IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
            //JSON序列化与反序列化的接口
            IJsonSerializer serializer = new JsonNetSerializer();
            //Base64编码器
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            //JWT编码器
            IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
            //   extraHeaders:
            //    任意一组额外的标题。即自定义使用的签名算法和加密类型
            //   payload:
            //    任意负载(必须可序列化为JSON)
            //   key:
            //     用于签名令牌的密钥
            //不加header表示使用默认的签名算法和加密类型
            var token = encoder.Encode(payload,secret);
            Console.WriteLine($"方式一生成的token为:[{token}]");
            JWTDecode(token);//通过第一种方式进行解码
        }

        /// 
        /// 获取JWT  方式二
        /// 
        public static void JWTEncode_2()
        {
           
            //使用Fluent API对JWT进行编译。
            var token = new JwtBuilder()
          .WithAlgorithm(new HMACSHA256Algorithm()) // 设置JWT算法
          .WithSecret(secret)//设置加密密钥
          .AddClaim("time",DateTime.Now)//设置时间
          .AddClaim("name", "李四")
          .Encode();//使用提供的依赖项对令牌进行编码
            Console.WriteLine($"方式二生成的token为:[{token}]");
            JWTDecode_2(token);

        }

        /// 
        /// 解析JWT  方式一
        /// 
        public static void JWTDecode(string token)
        {
            try
            {
                //JSON序列化与反序列化的接口
                IJsonSerializer serializer = new JsonNetSerializer();
                //UTC日期时间的提供程序。
                var provider = new UtcDateTimeProvider();
                //给定JWT,在不引发异常的情况下验证其签名的正确性
                IJwtValidator validator = new JwtValidator(serializer, provider);
                //Base64编码器
                IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                //对称 JWT签名的算法
                IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
                //JWT解码器
                IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
                //解析后的json
                //token 要解析的token
                //secret 解析token需要的秘钥
                //verify 是否验证签名(默认为true)
                var json = decoder.Decode(token, secret, verify: true);
                //输出解析后的json
                Console.WriteLine($"方式一解析后的json为:[{json}]");
            }
            catch (TokenExpiredException ex)
            {
                Console.WriteLine("令牌已过期:"+ex.ToString());
            }
            catch (SignatureVerificationException ex)
            {
                Console.WriteLine("签名有误:"+ex.ToString());
            }
            catch (Exception ex)
            {
                Console.WriteLine("解析json时出现异常:" + ex.ToString());
            }

        }

        /// 
        /// 解析JWT  方式二
        /// 
        public static void JWTDecode_2(string token)
        {
            try
            {
                var json = new JwtBuilder()
                .WithAlgorithm(new HMACSHA256Algorithm()) // 设置JWT算法
                .WithSecret(secret)//校验的秘钥
                .MustVerifySignature()//必须校验秘钥
                .Decode(token);//解析token
                //输出解析后的json
                Console.WriteLine($"方式二解析后的json为:[{json}]");
            }
            catch (Exception ex)
            {
                Console.WriteLine("解析json时出现异常:"+ ex.ToString());
            }

        }
    }
}

2、NETCore Web中使用JWT

​ (1)创建一个Web Api程序(步骤省略。。。)

​ (2)创建一个JWTService类

 public interface IJWTService
    {
        /// 
        /// 根据用户名获取token
        /// 
        /// 
        /// 
        string GetToken(string UserName);
    }

    /// 
    /// 生成JWT的service类
    /// 
    public class JWTService : IJWTService
    {
        private readonly IConfiguration _configuration;
        /// 
        /// 在构造函数中注入configuration以拿取appsettings.json中的内容
        /// 
        /// 
        public JWTService(IConfiguration configuration)
        {
            this._configuration = configuration;
        }


        /// 
        /// 根据用户名获取token
        /// 
        /// 
        /// 
        public string GetToken(string UserName)
        {
            //注:下面调用方法都是使用了默认的header
            //初始化payload
            Claim[] claims = new[]
            {
                new Claim(ClaimTypes.Name,UserName),
                new Claim("name","zhangsan"),
                new Claim("time",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
            };
            //生成对称秘钥
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["secret"]));
            //初始化签名凭证
            SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            /**
             *  Claims (Payload)
                Claims 部分包含了一些跟这个 token 有关的重要信息。 JWT 标准规定了一些字段,下面节选一些字段:
                iss: jwt签发者
                sub: jwt所面向的用户
                aud: 接收jwt的一方
                exp: jwt的过期时间,这个过期时间必须要大于签发时间
                nbf: 定义在什么时间之前,该jwt都是不可用的.
                iat: jwt的签发时间
                jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
                除了规定的字段外,可以包含其他任何 JSON 兼容的字段。
             * */
            var token = new JwtSecurityToken(
                issuer: _configuration["issuer"],//设置签发者
                audience: _configuration["audience"],//设置接收者
                claims: claims,//设置payload
                expires: DateTime.Now.AddMinutes(5),//5分钟有效期
                signingCredentials: creds);//初始化安全令牌参数
            //输出token
            string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
            return returnToken;
        }
    }

​ (3)appsetting.json中代码如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",

  //JWT  start
  "audience": "zhagnsan",
  "issuer": "lisi",
  "secret": "fc93cb07e1ad92898527100e58a1cf1d1e7f65e9a266a6f87f3c84feb541c7b3"
  //JWT  end
}

​ (4)Start.up中代码如下:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped();//将JWTService注入,那样就可以在构造函数中进行注入。如果没加进来,在构造函数中注入configuration是无用的
            services.AddControllers();
            #region JWT鉴权注入
            //1.Nuget引入程序包:Microsoft.AspNetCore.Authentication.JwtBearer 
            var ValidAudience = this.Configuration["audience"];
            var ValidIssuer = this.Configuration["issuer"];
            var SecurityKey = this.Configuration["secret"];
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  //默认授权机制名称   
                .AddJwtBearer(options =>
                     {
                         options.TokenValidationParameters = new TokenValidationParameters
                         {
                             ValidateIssuer = true,//是否在令牌期间验证签发者
                             ValidateAudience = true,//是否验证接收者
                             ValidateLifetime = true,//是否验证失效时间
                             ValidateIssuerSigningKey = true,//是否验证签名
                             ValidAudience = ValidAudience,//接收者
                             ValidIssuer = ValidIssuer,//签发者,签发的Token的人
                             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey))//拿到SHA256加密后的key
                         };
                     });
            #endregion
        }

        // 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.UseRouting();

            app.UseAuthorization();//使用Authorization

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

(5)AuthenticationController中代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AuthenticationCenter.Utils;
using JWT.Algorithms;
using JWT.Builder;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace AuthenticationCenter.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        #region 注入
        private IJWTService _iJWTService = null;//注入IJWTService接口
        private IConfiguration _configuration = null;//注入配置信息接口

        /// 
        /// 构造器中进行注入
        /// 
        /// 
        /// 
        /// 
        public AuthenticationController( IJWTService iJWTService, IConfiguration configuration)
        {
            this._iJWTService = iJWTService;
            this._configuration = configuration;
        }
        #endregion

        /// 
        /// 请求token
        /// 
        /// 
        /// 
        [Route("RequestToken")]
        [HttpGet]
        public string RequestToken(string name)
        {
            //如果等于admin那么就调用方法生成token,这里测试所以写死了
            if ("admin".Equals(name))
            {
                string token = this._iJWTService.GetToken(name);
                return JsonConvert.SerializeObject(new
                {
                    result = true,
                    token
                });
            }
            else
            {
                return JsonConvert.SerializeObject(new
                {
                    result = false,
                    token = "无法请求"
                });
            }
        }
        /// 
        /// 校验token并返回
        /// 
        /// 
        [HttpGet]
        [Route("CheckAuthorize")]
        // [Authorize] //Microsoft.AspNetCore.Authorization
        public IActionResult CheckAuthorize()
        {
            try
            {
                //获取claims
                var claims = base.HttpContext.AuthenticateAsync().Result.Principal.Claims.ToList();
                //获取请求的token
                var token = base.HttpContext.AuthenticateAsync().Result.Properties.Items.ToArray()[0].Value;
                string json =  JWTDecode(token);//解析token
                return new JsonResult(new
                {
                    Data = "已授权",
                    Type = "CheckAuthorize",
                    Claim = claims[0].Issuer,
                    Json = json
                });

            }
            catch (Exception ex)
            {
                return new JsonResult(new
                {
                    Data = "未授权",
                    Type = "CheckAuthorize",
                    Exception = ex.ToString()
                });
            }
        }

        /// 
        /// 解析JWT
        /// 
        /// 
        public string JWTDecode(string token)
        {
            try
            {
                var json = new JwtBuilder()
                .WithAlgorithm(new HMACSHA256Algorithm()) // 设置JWT算法
                .WithSecret(_configuration["secret"])//校验的秘钥
                .MustVerifySignature()//必须校验秘钥
                .Decode(token);//解析token
                //输出解析后的json
                Console.WriteLine($"方式二解析后的json为:[{json}]");
                return json;
            }
            catch (Exception ex)
            {
                Console.WriteLine("解析json时出现异常:" + ex.ToString());
                return "";
              
            }

        }
    }
}

(6)获取token

NETCore中使用JWT_第2张图片

(7)解析token,需要选择Authorization,Type选择Bearer Token

NETCore中使用JWT_第3张图片

参考资料:

​ (1)JWT官网https://jwt.io/

​ (2)JWT文档及源码:https://github.com/jwt-dotnet/jwt#JwtNet-ASPNET-Core

​ (3)B站朝夕视频:https://www.bilibili.com/video/BV1D7411y7Po

项目源码:https://github.com/xgysigned/NETCoreProject

你可能感兴趣的:(NETCore)