ASP.NET Core 8.0 WebApi 从零开始学习JWT登录认证

文章目录

  • 前言
  • 相关链接
  • Nuget选择
  • 知识补充
    • JWT不是加密算法
    • 可逆加密和不可逆加密
  • 普通Jwt(不推荐)
    • 项目环境
      • Nuget
    • 最小JWT测试
    • 在WebApi中简单使用
      • 简单使用
      • 运行结果
    • WebApi 授权,博客太老了,尝试失败
  • WebApi .net core 8.0 最新版Jwt (微软官方集成)
    • 重新新建一个Webapi
      • 简单Controller
    • 最简单的Jwt认证
      • 获取JwtConfig
      • 新建JwtHelper类
      • 完善JwtConfig
      • 授权
        • 授权测试
      • 能通过Jwt的请求
        • PostMan测试
        • 过期测试
      • Swagger 全局Header
    • 获取Jwt中的信息
      • 简单封装一下
    • Jwt授权模式基础讲解
  • 封装好的代码
  • 总结

前言

我一起写后端Api我都是直接裸连的,但是现在为了规范一些,我还是打算上一个JWT功能

相关链接

ASP.NET Web API 2系列(四):基于JWT的token身份认证方案

Jwt-dotnet github

Nuget选择

在这里插入图片描述
选好了模板,就进去看看号了,42M的下载量已经很高了,一般来说,只要超过500k的下载量,基本就是一个稳定的代码仓库了。

进去看看里面写的怎么样

在这里插入图片描述

可以看到写的还是比较清晰的

在这里插入图片描述

知识补充

JWT不是加密算法

JWT全名 JSON Web Token。Token这部分才是加密得出来的,JWT只是一个网页认证策略。

可逆加密和不可逆加密

无论是可逆加密还是不可逆加密,都需要一个自定义的【Key】来辅助加密。可逆加密就类似于一元二次方程,key就类似于修改方程各项的系数,【Key=125】得到【x^2+2x+5】。我的原文是【1】,得到的密文就是计算后的结果【8】。所以加密比解密要方便。

不可逆加密就是不能逆向解决的方程,比如高阶方程【x^7- x^3 +5x-3】,正向好算,逆向基本不可解。

所以现实使用的时候,可逆加密一般是解密后使用。因为可逆,所以可以用于复杂的敏感信息。
不可逆加密是判断加密后的的密文是否相同,比如密码,是不能泄漏的,所以我们的密码只能重置,因为系统也不知道原密码是什么。

普通Jwt(不推荐)

项目环境

  • visual studio 2022
  • .net core 8.0
  • win 10

Nuget

在这里插入图片描述
在这里插入图片描述

最小JWT测试

我们先不管JWT是如何添加到ASP.NET Core里面的,我们先测试JWT的加密和登录验证的功能。

/// <summary>
/// 加密密钥
/// </summary>
private static readonly string jwtKey = "TokenKey";

public record UserTest(string Name,string Account,string Password);

static void Main(string[] args)
{

    UserTest userTest = new UserTest("小王","admin","1dixa0d");
    Console.WriteLine("原文");
    Console.WriteLine(JsonConvert.SerializeObject(userTest));//{"Name":"小王","Account":"admin","Password":"1dixa0d"}

    var encoder = GetEncoder();
    string jwtToken = encoder.Encode(userTest,jwtKey);
    Console.WriteLine("加密");
    Console.WriteLine(jwtToken);//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoi5bCP546LIiwiQWNjb3VudCI6ImFkbWluIiwiUGFzc3dvcmQiOiIxZGl4YTBkIn0.C_tlDhHpkjAkJpRRdnqz6Jn2FmP0qONAI4oNl8Ye9wM

    var decoder =  GetDecoder();
    var decodeData =  decoder.Decode(jwtToken,jwtKey);
    Console.WriteLine("解密");
    Console.WriteLine(decodeData);//{"Name":"小王","Account":"admin","Password":"1dixa0d"}


    Console.WriteLine("Hello, World!");
}

/// <summary>
/// 获取加密解密
/// </summary>
/// <returns></returns>
public static IJwtEncoder GetEncoder()
{
    IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
    IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
    IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
    IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
    return encoder;
}

/// <summary>
/// 获取解密密钥
/// </summary>
/// <returns></returns>
public static IJwtDecoder GetDecoder()
{
    IJsonSerializer serializer = new JsonNetSerializer();
    IDateTimeProvider provider = new UtcDateTimeProvider();
    IJwtValidator validator = new JwtValidator(serializer, provider);
    IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
    IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
    IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
    return decoder;
}

在这里插入图片描述
这里的JWT的加密解密算法是可以自己搭配的,选择加密算法,这里我就不展开了。详细可以看官方文档
在这里插入图片描述

在WebApi中简单使用

首先我们之前用到了可逆的加密和解密。那我们就需要用一个判断是否过期的类

public class UserJwtLogin
{
    /// <summary>
    /// 用户Id,因为数据库的Id是唯一的,不会重复
    /// </summary>
    public long UserId { get; set; }    

    /// <summary>
    /// 过期时间
    /// </summary>
    public DateTime ExpireTime { get; set; }
}

详细的解决方案就在这个文章里面了,我就不照抄了,拾人牙慧。

ASP.NET Web API 2系列(四):基于JWT的token身份认证方案

我这里是这么写的

新建一个JwtHelper

public static class JwtHelper
{

    private static readonly string JwtKey = "这里填你的密钥";
    /// <summary>
    /// 获取加密解密
    /// </summary>
    /// <returns></returns>
    private static IJwtEncoder GetEncoder()
    {
        IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
        IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
        IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
        IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
        return encoder;
    }

    /// <summary>
    /// 获取解密密钥
    /// </summary>
    /// <returns></returns>
    private static IJwtDecoder GetDecoder()
    {
        IJsonSerializer serializer = new JsonNetSerializer();
        IDateTimeProvider provider = new UtcDateTimeProvider();
        IJwtValidator validator = new JwtValidator(serializer, provider);
        IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
        IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
        IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
        return decoder;
    }

    /// <summary>
    /// 加密
    /// </summary>
    public static string Encode(object payload)
    {
        var encoder = GetEncoder();
        var token = encoder.Encode(payload, JwtKey);
        return token;
    }

    /// <summary>
    /// 解密
    /// </summary>
    public static T Decode<T>(string token)
    {
        var decoder = GetDecoder();
        var data =  decoder.Decode(token,JwtKey);
        var res  = JsonConvert.DeserializeObject<T>(data);
        return res;
    }

    /// <summary>
    /// 解密,只返回Json文本
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public static string Decode(string token)
    {

        var decoder = GetDecoder();
        var data = decoder.Decode(token, JwtKey);
        return data;
    }

}

两个实体类

   public class UserJwtLogin
   {
       /// <summary>
       /// 用户Id,因为数据库的Id是唯一的,不会重复
       /// </summary>
       public long UserId { get; set; }    

       /// <summary>
       /// 过期时间
       /// </summary>
       public DateTime ExpireTime { get; set; }
   }
public class WebMsg
{

    public int Code { get; set; } = 200;

    public bool Success { get; set; } = true;

    public object Data {get; set; }

    public string Msg { get; set; } = "操作成功!";


    public WebMsg()
    {

    }


    public WebMsg(object data)
    {
        Data = data;
    }
}

简单使用

/// <summary>
/// Jwt登录
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class LoginController : ControllerBase
{
	//这个是我的Nlog配置,这里不做展开
    private NlogService nlogService;

    public LoginController(NlogService nlogService)
    {
        this.nlogService = nlogService;
    }



    /// <summary>
    /// JWT登录
    /// </summary>
    /// <param name="username">账号</param>
    /// <param name="password">密码</param>
    /// <returns></returns>
    [HttpGet]
    public WebMsg Login(string username, string password)
    {

        if (username == null || password == null)
        {
            return new WebMsg()
            {
                Msg = "登录信息为空",
                Success = false,
            };
        }
        if (username == "admin" && password == "123456")
        {
			//这里是模拟拿到数据库的User表的Id
            var pyload = new UserJwtLogin()
            {
                UserId = 291,
                ExpireTime = DateTime.Now.AddDays(1)
            };
            var token = JwtHelper.Encode(pyload);
            return new WebMsg(token);
        }
        else
        {
            return new WebMsg()
            {
                Msg = "登录失败,账号或者密码错误",
                Success = false,
            };
        }

    }

    /// <summary>
    /// Jwt解密
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    [HttpGet]
    public WebMsg Decode(string token)
    {
        try
        {
            var res = JwtHelper.Decode(token);
            return new WebMsg(res);
        }
        catch (Exception ex)
        {
            nlogService.Error("登录解析失败:" + token);
            nlogService.Error(ex.Message);
            return new WebMsg() { 
                Msg = "登录解析失败:" + token,
                Success = false,
            };
        }
    }
}

运行结果

在这里插入图片描述

{
  "code": 200,
  "success": true,
  "data": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjI5MSwiRXhwaXJlVGltZSI6IjIwMjQtMDMtMTRUMTM6MjE6MzYuNjE3NDk0KzA4OjAwIn0.sxh9sM4gQoCfFfim-MQSsHqDQX3Dji3FZaEu4t06D1s",
  "msg": "操作成功!"
}

在这里插入图片描述

{
  "code": 200,
  "success": true,
  "data": "{\"UserId\":291,\"ExpireTime\":\"2024-03-14T13:21:36.617494+08:00\"}",
  "msg": "操作成功!"
}

WebApi 授权,博客太老了,尝试失败

这里我们就用简单的授权,复杂的授权可以自己去看微软官方文档

ASP.NET Core 中的简单授权

在这里插入图片描述

然后我发现.net core 8.0的认证方式好像变了,我按照博客的写法发现不让我重写了
在这里插入图片描述
这个方法是.net framework上面用的,.net core 用不了。我想还是算了,用.net core 的方法好了
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

WebApi .net core 8.0 最新版Jwt (微软官方集成)

我看了一天的博客,终于解决了JWT认证的问题。

在没有 ASP.NET CoreIdentity的情况下使用cookie身份验证

ASP.NET Core 6.0 添加 JWT 认证和授权

.Net Core WebApi集成JWT实现身份认证

在这里插入图片描述

重新新建一个Webapi

这里我就不多说了

.NET Core webapi 从零开始在IIS上面发布后端接口

我们还是要改一下的,因为之前的我测试的时候,发现Builder里面的代码实在是太多了,这里我们分开一下


using Microsoft.OpenApi.Models;
using System.Reflection;

namespace JtwTestWebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            var MyPolicy = "MyPolicy";
            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();
            //为了防止配套太多,代码混乱。这里自定义了一个方法
            AddMyService(builder);
            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                
            }
            app.UseSwagger();
            app.UseSwaggerUI();
            app.UseStatusCodePagesWithRedirects("/swagger/index.html");
            app.UseHttpsRedirection();

            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }

        /// <summary>
        /// 为了防止代码过于臃肿,将新的配置放在这里写
        /// </summary>
        /// <param name="builder"></param>
        public static void AddMyService(WebApplicationBuilder builder)
        {
            builder.Services.AddCors(options =>
            {
                options.AddPolicy("MyPolicy", policy =>
                {
                    policy.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod();
                });
            });
            builder.Services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "API标题",
                    Description = $"API描述,v1版本"
                });
                var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                //IncludeXmlComments 第二参数 true 则显示 控制器 注释
                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename), true);
            });
        }
    }
}

简单Controller

public class WebMsg
{

    public object Data { get; set; }

    public bool Success { get; set; } = true;

    public string Msg { get; set; } = "操作成功!";

    public WebMsg()
    {

    }

    public WebMsg(object data)
    {
        Data = data;
    }
}
/// <summary>
/// 测试控制器
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
    /// <summary>
    /// 测试返回值
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public WebMsg GetTest()
    {
        return new WebMsg("测试返回值");
    }

}

在这里插入图片描述

最简单的Jwt认证

我们知道Jwt其实就是生成和验证两个功能。微软把这个功能集成到一个JwtBearer库里面了。

在这里插入图片描述
为了方便Json打印,添加个Newtonsoft
在这里插入图片描述

获取JwtConfig

我们在appsetting.json里面添加

  "JwtConfig": {
    "SecretKey": "123123123123", // 密钥   可以是guid 也可以是随便一个字符串
    "Issuer": "XiaoWang", // 颁发者
    "Audience": "XiaoWang", // 接收者
    "Expired": 30 // 过期时间(30min)
  }

在这里插入图片描述

 public class JwtConfig
 {
     /// <summary>
     /// 密钥
     /// </summary>
     public string SecretKey { get; set; }   

     /// <summary>
     /// 发布者
     /// </summary>
     public string Issuer { get; set; }

     /// <summary>
     /// 接受者
     /// </summary>
     public string Audience { get; set; }

     /// <summary>
     /// 过期时间(min)
     /// </summary>
     public int Expired { get; set; }
 }

获取Config

var jwtConfig = new JwtConfig();
builder.Configuration.Bind("JwtConfig",jwtConfig);

在这里插入图片描述
在这里插入图片描述

新建JwtHelper类

 public class JwtHelper
 {

     public JwtConfig JwtConfig { get; set; }
     public JwtHelper()
     {

     }

     /// <summary>
     /// 添加Jwt服务
     /// </summary>
     public void AddJwtService()
     {

     }

     /// <summary>
     /// 返回
     /// </summary>
     /// <returns></returns>
     public string GetJwtToken()
     {
         //简单测试一下
         var token = JsonConvert.SerializeObject(JwtConfig);
         return token;
     }


 }

我们应该使用Ioc的方式注入JwtHelper

var jwtConfig = new JwtConfig();
builder.Configuration.Bind("JwtConfig",jwtConfig);
var jwtHelper = new JwtHelper() {
    JwtConfig = jwtConfig
};
//将JwtHelper添加到Services里面
builder.Services.AddSingleton<JwtHelper>(jwtHelper);

在这里插入图片描述

然后在控制器里面获取Token

/// <summary>
/// 测试控制器
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
    private JwtHelper jwtHelper;

    /// <summary>
    /// 通过Ioc得到Jwt
    /// </summary>
    /// <param name="jwtHelper"></param>
    public TestController(JwtHelper jwtHelper) {
        this.jwtHelper = jwtHelper;
    }
    /// <summary>
    /// 测试返回值
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public WebMsg GetTest()
    {
        return new WebMsg("测试返回值");
    }


    /// <summary>
    /// 获取JwtToken
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public WebMsg GetJwtToken()
    {
        var token = jwtHelper.GetJwtToken();
        return new WebMsg(token);
    }
}

在这里插入图片描述
接下来我们修改一下GetJwtToken这个函数即可

完善JwtConfig

using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace JtwTestWebApi.Models
{
    public class JwtConfig
    {
        /// <summary>
        /// 密钥
        /// </summary>
        public string SecretKey { get; set; }

        /// <summary>
        /// 发布者
        /// </summary>
        public string Issuer { get; set; }

        /// <summary>
        /// 接受者
        /// </summary>
        public string Audience { get; set; }

        /// <summary>
        /// 过期时间(min)
        /// </summary>
        public int Expired { get; set; }

        /// <summary>
        /// 生效时间
        /// </summary>
        public DateTime NotBefore => DateTime.Now;

        /// <summary>
        /// 过期时间
        /// </summary>
        public DateTime Expiration => DateTime.Now.AddMinutes(Expired);

        /// <summary>
        /// 密钥Bytes
        /// </summary>
        private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));

        /// <summary>
        /// 加密后的密钥,使用HmacSha256加密
        /// </summary>
        public SigningCredentials SigningCredentials =>
            new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);
    }
}

在JwtHelper里面使用

/// <summary>
/// 最简单的JwtToken
/// </summary>
/// <returns></returns>
public string GetJwtToken()
{
    var claims = new List<Claim>();
    var jwtSecurityToken = new JwtSecurityToken(
            JwtConfig.Issuer,
            JwtConfig.Audience,
            claims,
            JwtConfig.NotBefore,
            JwtConfig.Expiration,
            JwtConfig.SigningCredentials
        );
    var token = new JwtSecurityTokenHandler().WriteToken( jwtSecurityToken );
    return token;
}
       

运行报错,说明密钥的长度太短了,我们加长一下
在这里插入图片描述
在这里插入图片描述
返回成功!

在这里插入图片描述

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MTAzODUyODgsImV4cCI6MTcxMDM4NzA4OCwiaXNzIjoiWGlhb1dhbmciLCJhdWQiOiJYaWFvV2FuZyJ9.v7UbQOba7VoNgoiRsoIQkFJKQTFBMLJlYEKBIfdFV4o

授权

加密了,肯定要有对应的授权

在JwtConfig里面添加对应的授权

public class JwtConfig
{
    /// <summary>
    /// 密钥
    /// </summary>
    public string SecretKey { get; set; }

    /// <summary>
    /// 发布者
    /// </summary>
    public string Issuer { get; set; }

    /// <summary>
    /// 接受者
    /// </summary>
    public string Audience { get; set; }

    /// <summary>
    /// 过期时间(min)
    /// </summary>
    public int Expired { get; set; }

    /// <summary>
    /// 生效时间
    /// </summary>
    public DateTime NotBefore => DateTime.Now;

    /// <summary>
    /// 过期时间
    /// </summary>
    public DateTime Expiration => DateTime.Now.AddMinutes(Expired);

    /// <summary>
    /// 密钥Bytes
    /// </summary>
    private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));

    /// <summary>
    /// 加密后的密钥,使用HmacSha256加密
    /// </summary>
    public SigningCredentials SigningCredentials =>
        new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);

    /// <summary>
    /// 认证用的密钥
    /// </summary>
    public SymmetricSecurityKey SymmetricSecurityKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));
}

在这里插入图片描述

在JwtHelper中

/// <summary>
/// 添加Jwt服务
/// </summary>
public void AddJwtService(IServiceCollection services)
{
    services.AddAuthentication(option =>
    {
        //认证middleware配置
        option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            //Token颁发机构
            ValidIssuer = JwtConfig.Issuer,
            //颁发给谁
            ValidAudience = JwtConfig.Audience,
            //这里的key要进行加密
            IssuerSigningKey = JwtConfig.SymmetricSecurityKey,
            //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
            ValidateLifetime = true,
        };
    });
}

调用JwtHelper

  var jwtConfig = new JwtConfig();
  builder.Configuration.Bind("JwtConfig",jwtConfig);
  var jwtHelper = new JwtHelper() {
      JwtConfig = jwtConfig
  };
  //将JwtHelper添加到Services里面
  builder.Services.AddSingleton<JwtHelper>(jwtHelper);
  jwtHelper.AddJwtService(builder.Services);

在app中启用

            app.UseHttpsRedirection();
            app.UseAuthentication();//要在授权之前认证,这个和[Authorize]特性有关
            app.UseAuthorization();

一定要在za之前使用ca
在这里插入图片描述

授权测试

我们接口最好单独开一个获取Token的接口

TestController

using JtwTestWebApi.Models;
using JtwTestWebApi.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace JtwTestWebApi.Controllers
{
    /// <summary>
    /// 测试控制器
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private JwtHelper jwtHelper;

        /// <summary>
        /// 通过Ioc得到Jwt
        /// </summary>
        /// <param name="jwtHelper"></param>
        public TestController(JwtHelper jwtHelper) {
            this.jwtHelper = jwtHelper;
        }
        /// <summary>
        /// 测试返回值
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public WebMsg GetTest()
        {
            return new WebMsg("测试返回值");
        }


        /// <summary>
        /// 获取JwtToken
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public WebMsg GetJwtToken()
        {
            var token = jwtHelper.GetJwtToken();
            return new WebMsg(token);
        }

        /// <summary>
        /// 可以在方法前面加Authorize
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [HttpGet]
        public WebMsg GetByJwt()
        {
            return new WebMsg("Jwt测试成功!");
        }
    }
}

JtwTestWebApi

如果我们在类前面添加了[Authorize],下面全部的接口都有Jwt认证。想放开Jtw认证,使用[AllowAnonymous]标记方法即可

using JtwTestWebApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace JtwTestWebApi.Controllers
{
    /// <summary>
    /// Jwt测试类
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    [Authorize]
    public class JwtTestController : ControllerBase
    {
        /// <summary>
        /// 不需要Jwt
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpGet]
        public WebMsg NoJwtGet()
        {
            return new WebMsg("我不需要Jwt");
        }


        /// <summary>
        /// 需要Jwt
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public WebMsg JwtGet()
        {
            return new WebMsg("我需要Jwt");
        }
    }
}

认证失败的结果

在这里插入图片描述

能通过Jwt的请求

既然我们拦截了请求,我们也得对符合规范的请求放行。那什么样的请求能放行呢?在Header中的【Authorization】中添加【Bearer {token}】才能放行。

PostMan测试

在这里插入图片描述
在这里插入图片描述

测试成功!
在这里插入图片描述

过期测试

.NetCore JWT token过期时间设置

Jwt的过期是由两个数据综合相加的。一个是生成Token的过期时间

在这里插入图片描述
一个是缓冲时间,默认5分钟。因为服务器的时间可能不同步。

在这里插入图片描述
所以总过期时间=过期时间+缓冲时间

在这里插入图片描述
为了更简单的获取Token,我们直接把返回的Token自带[Bearer ]这个前缀好了

Swagger 全局Header

在JwtHelper中添加全局静态方法

/// <summary>
/// Swagger添加Jwt功能
/// </summary>
/// <param name="options"></param>
public static void SwaggerAddJwtHeader(SwaggerGenOptions options)
{
    options.AddSecurityRequirement(new OpenApiSecurityRequirement()
        {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer",
                    }
                },
            new string[] { }
            }
        });
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
        Name = "Authorization",//jwt默认的参数名称
        In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
        Type = SecuritySchemeType.ApiKey,
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

获取Jwt中的信息

因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头,里面其实是可以解密的

 /// <summary>
 /// 获取Jwt的信息
 /// </summary>
 /// <param name="request"></param>
 /// <returns></returns>
 public IEnumerable<Claim> Decode(HttpRequest request)
 {
     
     var authorization = request.Headers["Authorization"].ToString();
     //因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头
     var auth = authorization.Split(" ")[1];
     var handler = new JwtSecurityTokenHandler();
     //反解密,获取其中的Claims
     var payload = handler.ReadJwtToken(auth).Payload;
     var claims = payload.Claims;
     return claims;
 }

解密能拿到是因为我们加密的时候就已经放进去了

 /// <summary>
 /// 添加claims信息
 /// </summary>
 /// <param name="claims"></param>
 /// <returns></returns>
 public string GetJwtToken(List<Claim> claims)
 {
     var jwtSecurityToken = new JwtSecurityToken(
             JwtConfig.Issuer,
             JwtConfig.Audience,
             claims,
             JwtConfig.NotBefore,
             JwtConfig.Expiration,
             JwtConfig.SigningCredentials
         );
     var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
     token = "Bearer " + token;
     return token;
 }
 /// <summary>
 /// 获取JwtToken
 /// </summary>
 /// <returns></returns>
 [HttpGet]
 public WebMsg GetJwtToken2()
 {
 	//我们加密的时候,就放进去了一些额外的信息
     var token = jwtHelper.GetJwtToken(new List<Claim>()
     {
         new Claim("UserId","2"),
         new Claim("UserName","3")
     });
     return new WebMsg(token);
 }
/// <summary>
/// 可以在方法前面加Authorize
/// </summary>
/// <returns></returns>
[Authorize]
[HttpGet]
public WebMsg GetByJwt()
{
	//获取解密后的Claim
    var dic = jwtHelper.Decode(this.Request);
    return new WebMsg("Jwt测试成功!");
}

在这里插入图片描述
这样我们就可以把一些比较隐私的数据放在里面,比如用户Id,这样防止泄漏。

简单封装一下

using JtwTestWebApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JtwTestWebApi.Utils
{
    public class JwtHelper
    {

        public JwtConfig JwtConfig { get; set; }
        public JwtHelper()
        {

        }

        /// <summary>
        /// 添加Jwt服务
        /// </summary>
        public void AddJwtService(IServiceCollection services)
        {
            services.AddAuthentication(option =>
            {
                //认证middleware配置
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    //Token颁发机构
                    ValidIssuer = JwtConfig.Issuer,
                    //颁发给谁
                    ValidAudience = JwtConfig.Audience,
                    //这里的key要进行加密
                    IssuerSigningKey = JwtConfig.SymmetricSecurityKey,
                    //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = true,
                    RequireExpirationTime = true,
                };
            });
        }

        /// <summary>
        /// 最简单的JwtToken
        /// </summary>
        /// <returns></returns>
        public string GetJwtToken()
        {
            var claims = new List<Claim>();
            var jwtSecurityToken = new JwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token = "Bearer " + token;
            return token;
        }

        /// <summary>
        /// 添加claims信息
        /// </summary>
        /// <param name="claims"></param>
        /// <returns></returns>
        public string GetJwtToken(List<Claim> claims)
        {
            var jwtSecurityToken = new JwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token = "Bearer " + token;
            return token;
        }

        /// <summary>
        /// UserModel类Token
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public string GetJwtToken(JwtUserModel user)
        {
            var claims = new List<Claim>() {
                new Claim("UserId",user.UserId.ToString()),
                new Claim("UserName",user.UserName),
                new Claim("UserType",user.UserType.ToString()),
            };
            return GetJwtToken(claims);
        }

        /// <summary>
        /// Swagger添加Jwt功能
        /// </summary>
        /// <param name="options"></param>
        public static void SwaggerAddJwtHeader(SwaggerGenOptions options)
        {
            options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer",
                            }
                        },
                    new string[] { }
                    }
                });
            options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
                Name = "Authorization",//jwt默认的参数名称
                In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                Type = SecuritySchemeType.ApiKey,
                BearerFormat = "JWT",
                Scheme = "Bearer"
            });
        }

        /// <summary>
        /// 获取Jwt的信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public IEnumerable<Claim> Decode(HttpRequest request)
        {

            var authorization = request.Headers["Authorization"].ToString();
            //因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头
            var auth = authorization.Split(" ")[1];
            var handler = new JwtSecurityTokenHandler();
            //反解密,获取其中的Claims
            var payload = handler.ReadJwtToken(auth).Payload;
            var claims = payload.Claims;
            return claims;
        }

        /// <summary>
        /// 解析得到User
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public JwtUserModel DecodeToUser(HttpRequest request)
        {
            var claims = Decode(request);
            var user = new JwtUserModel()
            {
                UserId = claims.Where(t => t.Type == "UserId").First().Value,
                UserName = claims.Where(t => t.Type == "UserName").First().Value,
                UserType = claims.Where(t => t.Type == "UserType").First().Value
            };
            return user;
        }
    }
}

[HttpGet]
public WebMsg GetJwtToken3()
{
    var token = jwtHelper.GetJwtToken(new JwtUserModel()
    {
        UserName = "小王",
        UserId = "32",
        UserType = "admin"
    });
    return new WebMsg(token);
}
[Authorize]
[HttpGet]
public WebMsg GetByJwt3()
{
    var dic = jwtHelper.DecodeToUser(this.Request);

    return new WebMsg("Jwt测试成功!");
}

运行得到结果
在这里插入图片描述

Jwt授权模式基础讲解

介绍三种授权方式(Policy、Role、Scheme),Scheme这种用的太少了,我们就简单讲解一下Policy和Role这两种方式。Policy和Role的核心就在于我们封装Token的时候添加的Claim对象。大家看到这里就会发现,其实Jwt的核心就是装包和拆包。网页请求Token的时候拿到了个包裹,网页发送Http的时候解开这个包裹。

我写了一会,感觉有点累了。这里有个别人写好的代码,有兴趣的自己去看吧。

ASP.NET Core 6.0 添加 JWT 认证和授权

封装好的代码

using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace JtwTestWebApi.Models
{
    public class JwtConfig
    {
        /// <summary>
        /// 密钥
        /// </summary>
        public string SecretKey { get; set; }

        /// <summary>
        /// 发布者
        /// </summary>
        public string Issuer { get; set; }

        /// <summary>
        /// 接受者
        /// </summary>
        public string Audience { get; set; }

        /// <summary>
        /// 过期时间(min)
        /// </summary>
        public int Expired { get; set; }

        /// <summary>
        /// 生效时间
        /// </summary>
        public DateTime NotBefore => DateTime.Now;

        /// <summary>
        /// 过期时间
        /// </summary>
        public DateTime Expiration => DateTime.Now.AddMinutes(Expired);

        /// <summary>
        /// 密钥Bytes
        /// </summary>
        private SecurityKey SigningKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));

        /// <summary>
        /// 加密后的密钥,使用HmacSha256加密
        /// </summary>
        public SigningCredentials SigningCredentials =>
            new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);

        /// <summary>
        /// 认证用的密钥
        /// </summary>
        public SymmetricSecurityKey SymmetricSecurityKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));



    }
}

using JtwTestWebApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JtwTestWebApi.Utils
{
    public class JwtHelper
    {

        public JwtConfig JwtConfig { get; set; }
        public JwtHelper()
        {

        }

        /// <summary>
        /// 添加Jwt服务
        /// </summary>
        public void AddJwtService(IServiceCollection services)
        {
            services.AddAuthentication(option =>
            {
                //认证middleware配置
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    //Token颁发机构
                    ValidIssuer = JwtConfig.Issuer,
                    //颁发给谁
                    ValidAudience = JwtConfig.Audience,
                    //这里的key要进行加密
                    IssuerSigningKey = JwtConfig.SymmetricSecurityKey,
                    //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                    ValidateLifetime = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = true,
                    RequireExpirationTime = true,
                };
            });
        }

        /// <summary>
        /// 最简单的JwtToken
        /// </summary>
        /// <returns></returns>
        public string GetJwtToken()
        {
            var claims = new List<Claim>();
            var jwtSecurityToken = new JwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token = "Bearer " + token;
            return token;
        }

        /// <summary>
        /// 添加claims信息
        /// </summary>
        /// <param name="claims"></param>
        /// <returns></returns>
        public string GetJwtToken(List<Claim> claims)
        {
            var jwtSecurityToken = new JwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token = "Bearer " + token;
            return token;
        }

        /// <summary>
        /// UserModel类Token
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public string GetJwtToken(JwtUserModel user)
        {
            var claims = new List<Claim>() {
                new Claim("UserId",user.UserId.ToString()),
                new Claim("UserName",user.UserName),
                new Claim("UserType",user.UserType.ToString()),
            };
            return GetJwtToken(claims);
        }

        /// <summary>
        /// Swagger添加Jwt功能
        /// </summary>
        /// <param name="options"></param>
        public static void SwaggerAddJwtHeader(SwaggerGenOptions options)
        {
            options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer",
                            }
                        },
                    new string[] { }
                    }
                });
            options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
                Name = "Authorization",//jwt默认的参数名称
                In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                Type = SecuritySchemeType.ApiKey,
                BearerFormat = "JWT",
                Scheme = "Bearer"
            });
        }

        /// <summary>
        /// 获取Jwt的信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public IEnumerable<Claim> Decode(HttpRequest request)
        {

            var authorization = request.Headers["Authorization"].ToString();
            //因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头
            var auth = authorization.Split(" ")[1];
            var handler = new JwtSecurityTokenHandler();
            //反解密,获取其中的Claims
            var payload = handler.ReadJwtToken(auth).Payload;
            var claims = payload.Claims;
            return claims;
        }

        /// <summary>
        /// 解析得到User
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public JwtUserModel DecodeToUser(HttpRequest request)
        {
            var claims = Decode(request);
            var user = new JwtUserModel()
            {
                UserId = claims.Where(t => t.Type == "UserId").First().Value,
                UserName = claims.Where(t => t.Type == "UserName").First().Value,
                UserType = claims.Where(t => t.Type == "UserType").First().Value
            };
            return user;
        }
    }
}

namespace JtwTestWebApi.Models
{
    public class JwtUserModel
    {

        public string UserId { get; set; }

        public string UserName { get; set; }

        public string UserType { get; set; }
    }
}


using JtwTestWebApi.Models;
using JtwTestWebApi.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;

namespace JtwTestWebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            var MyPolicy = "MyPolicy";
            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();
            //为了防止配套太多,代码混乱。这里自定义了一个方法
            AddMyService(builder);
            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                
            }
            app.UseSwagger();
            app.UseSwaggerUI();
            app.UseStatusCodePagesWithRedirects("/swagger/index.html");
            app.UseHttpsRedirection();
            app.UseAuthentication();//要在授权之前认证,这个和[Authorize]特性有关
            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }

        /// <summary>
        /// 为了防止代码过于臃肿,将新的配置放在这里写
        /// </summary>
        /// <param name="builder"></param>
        public static void AddMyService(WebApplicationBuilder builder)
        {
            #region 默认的Webapi配置
            builder.Services.AddCors(options =>
            {
                options.AddPolicy("MyPolicy", policy =>
                {
                    policy.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod();
                });
            });
            builder.Services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "API标题",
                    Description = $"API描述,v1版本"
                });
                var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                //IncludeXmlComments 第二参数 true 则显示 控制器 注释
                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename), true);
                JwtHelper.SwaggerAddJwtHeader(options);
            });
            #endregion 

            #region 添加Jwt服务
            var jwtConfig = new JwtConfig();
            builder.Configuration.Bind("JwtConfig",jwtConfig);
            var jwtHelper = new JwtHelper() {
                JwtConfig = jwtConfig
            };
            //将JwtHelper添加到Services里面
            builder.Services.AddSingleton<JwtHelper>(jwtHelper);
            jwtHelper.AddJwtService(builder.Services);
            #endregion

        }
    }
}

appsettings.json中添加

  "JwtConfig": {
    "SecretKey": "lisheng741@qq.comlisheng741@qq.com",
    "Issuer": "WebAppIssuer",
    "Audience": "WebAppAudience",
    "Expired": 30 // 过期时间(30min)
  }

总结

Jwt其实也不是特别难,就是第一次配置的时候容易被绕晕。Jwt的策略我暂时先跳过了,对于解决普通问题一般来说已经够用了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/456226.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【投稿优惠-EI稳定检索】2024年图像处理与机械系统工程国际学术会议 (ICIPMSE 2024)

【投稿优惠-EI稳定检索】2024年图像处理与机械系统工程国际学术会议 (ICIPMSE 2024) 大会主题: (主题包括但不限于, 更多主题请咨询会务组苏老师) 图像处理 基于图像的渲染 计算机视觉 可视化分析 模式识别 3D打印 渲染和动画 渲染技术 电脑动画 基于草图的建模 机械…

详解Python中%r和%s的区别及用法

首先看下面的定义&#xff1a; %r用rper()方法处理对象 %s用str()方法处理对象 函数str() 用于将值转化为适于人阅读的形式&#xff0c;而repr() 转化为供解释器读取的形式&#xff08;如果没有等价的语法&#xff0c;则会发生SyntaxError 异常&#xff09; 某对象没有适于人…

【leetcode+深度/广度优先搜索】841. 钥匙和房间 (DFS,BFS)

leetcode-cn&#xff1a;leetcode面试75道精华&#xff1a;https://leetcode.cn/studyplan/leetcode-75/ 841.钥匙和房间&#xff1a;https://leetcode.cn/problems/keys-and-rooms/description/ 一、题目&#xff1a;841. 钥匙和房间 有 n 个房间&#xff0c;房间按从 0 到 n…

零代码开发的优势 零代码平台开发的好处

随着数字化浪潮的推进&#xff0c;企业对于数据驱动的需求越来越高&#xff0c;而零代码快速开发平台正是满足这一需求的重要工具之一。零代码开发平台是一种无需编写代码即可开发应用程序的平台&#xff0c;它可以让用户通过拖、拉、拽的方式快速创建高度定制化的应用。这种平…

VC++ BitBlt函数学习

1 BitBlt BitBlt函数执行与像素矩形相对应的颜色数据的位块传输,从指定的源设备上下文传输到目标设备上下文。 把位块从一个DC传到另一个DC; VC单文档工程,写3句代码如下; void CDeskdcView::OnDraw(CDC* pDC) {CDeskdcDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);//…

【老旧小区用电安全谁能管?】安科瑞智慧用电安全管理系统解决方案

行业背景 电气火灾指由电气故障引发的火灾。每年以30%的比例高居各类火灾原因之首。以50%到80%的比例高居重特大火灾之首。已成为业界重点关注的对象并为此进行着孜孜不倦的努力。 国务院安委会也于2017年5月至2020年4月年开展了为期3年的电气火灾综合治理工作。在各界努力的…

6. C++ 钻石继承与虚继承

1. 钻石继承与虚继承 2. 什么是钻石继承&#xff1f; ANSWER&#xff1a;假设我们已经有了两个类Father1和Father2&#xff0c;他们都是类GrandFather的子类。现在又有一个新类Son&#xff0c;这个新类通过多继承机制对类Father1和Father2都进行了继承&#xff0c;此时类Gran…

Pulsar IO实战

一、引言 今天跟着 官方文档 基于docker玩一把Pulsar IO吧 二、概要 在用户能够轻松的将消息队列跟其他系统(数据库、其他消息系统)一起使用时&#xff0c;消息队列的作用才是最强大的。而Pulsar IO connectors可以让你很轻松的创建、部署以及管理这些跟外部系统的连接&#…

在SwiftUI中使用Buider模式创建复杂组件

在SwiftUI中使用Buider模式创建复杂组件 我们在前面的博客闲聊SwiftUI中的自定义组件中聊到了如何在SwiftU中创建自定义组件。 在那里&#xff0c;我们创建了一个非常简单的组件RedBox&#xff0c;它将展示内容增加一个红色的边框。 RedBox非常简单&#xff0c;我们用普通的方…

面试六--TCP粘包问题

1.流式传输协议 流式传输协议&#xff08;Streaming Protocol&#xff09;是一种用于在网络上传输数据的通信协议&#xff0c;它允许数据以连续的流的形式进行传输&#xff0c;而不是一次性发送完整的数据包。流式传输协议即协议的内容是像流水一样的字节流&#xff0c;内容与内…

Go——数组

Golang Array和以往认知的数组有很大的。 数组是同一种数据类型的固定长度的序列。数组定义&#xff1a;var a[len] int&#xff0c;比如&#xff1a;var a [5]int&#xff0c;数组长度必须是常量&#xff0c;且类型的组成部分。一旦定义&#xff0c;长度不能变。长度是数组类…

Focal and Global Knowledge Distillation forDetectors

摘要 文章指出&#xff0c;在目标检测中&#xff0c;教师和学生在不同领域的特征差异很大&#xff0c;尤其是在前景和背景中。如果我们 平等地蒸馏它们&#xff0c;特征图之间的不均匀差异将对蒸馏产生负面影响。因此&#xff0c;我们提出了局部和全局蒸馏。局部蒸馏分离前景和…

力扣101---对称二叉树(简单题)

题目描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 非递归Java代…

Leetcode 1514 概率最大的路径

文章目录 1. 题目描述2. 我的尝试 1. 题目描述 原题链接&#xff1a;Leetcode 1514 概率最大的路径 给你一个由 n 个节点&#xff08;下标从 0 开始&#xff09;组成的无向加权图&#xff0c;该图由一个描述边的列表组成&#xff0c;其中 edges[i] [a, b] 表示连接节点 a 和 b…

C#,数值计算,希尔伯特矩阵(Hilbert Matrix)的算法与源代码

Hilbert, David (1862-1943) 1 希尔伯特(Hilbert) 德国数学家,在《几何学基础》中提出了第一套严格的几何公理(1899年)。他还证明了自己的系统是自洽的。他发明了一条简单的空间填充曲线,即埃里克魏斯汀的数学世界,即希尔伯特曲线,埃里克魏斯汀的数学世界,并证明了不…

win11创建本地局域网网站

前言 本篇文章介绍在windows11环境下通过IIS(Internet Information Services)管理器创建局域网网站 启用windows相关功能 键盘win R打开运行窗口输入optionalfeatures&#xff0c;回车打开windows功能界面选中下面这几项,点击确定&#xff0c;稍等即可 打开IIS 按下win键…

3.1_6 基本地址变换机构

文章目录 3.1_6 基本地址变换机构&#xff08;一&#xff09;基本地址变换机构&#xff08;二&#xff09;对页表项大小的进一步探讨 总结 3.1_6 基本地址变换机构 提示&#xff1a; 重点理解、记忆**基本地址变换机构&#xff08;即用于实现逻辑地址到物理地址转换的一组硬件机…

网络层:地址解析协议ARP、网际控制报文协议ICMP、虚拟专用网络VPN、网络地址转换NAT

文章目录 地址解析协议ARP解决的问题ARP解析流程ARP高速缓存 网际控制报文协议ICMPICMP报文的种类ICMP差错报告报文ICMP询问报文 ICMP应用举例分组网间探测PING(Packet InterNet Groper)traceroute(tracert)确定路径的MTU 虚拟专用网络专用地址虚拟专用网络远程接入VPN(remote …

2024年文学、历史与艺术设计国际会议(ICLHAD2024)

2024年文学、历史与艺术设计国际会议(ICLHAD2024) 一、【会议简介】 2024国际文学、历史和艺术设计会议&#xff08;IACLHAD 2024&#xff09;将在中国杭州举行。本次大会将继续遵循学术性和国际性原则&#xff0c;邀请国内外高校、科研院所、企事业单位的文史美术设计领域的…

力扣每日一题 最大二进制奇数 模拟 贪心

Problem: 2864. 最大二进制奇数 由于奇数的二进制末尾一定是 111&#xff0c;我们可以把一个 111 放在末尾&#xff0c;其余的 111 全部放在开头&#xff0c;这样构造出的奇数尽量大。 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class…