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授权模式基础讲解
      • 简单的【角色授权】
        • 获取不同权限的Token
        • 不同权限的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_tlDhHpkjAkJpRRdnqz6Jn2FmP0qONAI4oNl8Ye9wMvar 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();//序列化JsonIBase64UrlEncoder 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();//序列化JsonIBase64UrlEncoder 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表的Idvar 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/swashbucklebuilder.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();//反解密,获取其中的Claimsvar 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()
{//获取解密后的Claimvar 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();//反解密,获取其中的Claimsvar 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 认证和授权

简单的【角色授权】

获取不同权限的Token

获取不同权限的Token

/// <summary>
/// 获取Role = admin的Token
/// </summary>
/// <returns></returns>
[HttpGet]
public WebMsg GetJwtToken_Admin()
{var token = jwtHelper.GetJwtToken(new List<Claim>(){new Claim(ClaimTypes.Role,"admin")});return new WebMsg(token);
}
/// <summary>
/// 获取Role = user
/// </summary>
/// <returns></returns>
[HttpGet]
public WebMsg GetJwtToken_User()
{var token = jwtHelper.GetJwtToken(new List<Claim>(){new Claim(ClaimTypes.Role,"user")});return new WebMsg(token);
}/// <summary>
/// 获取Role = admin和user
/// </summary>
/// <returns></returns>
[HttpGet]
public WebMsg GetJwtToken_UserAndAdmin()
{var token = jwtHelper.GetJwtToken(new List<Claim>(){new Claim(ClaimTypes.Role,"user"),new Claim(ClaimTypes.Role,"admin")});return new WebMsg(token);
}
不同权限的jwt认证
/// <summary>
/// 需要role=admin
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Roles = "admin")]
public WebMsg AdminGet()
{return new WebMsg("admin");
}
/// <summary>
/// role=user
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Roles = "user")]
public WebMsg UserGet()
{return new WebMsg("user");
}/// <summary>
/// role = admin或user
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Roles = "admin,user")]
public WebMsg AdminOrUserGet()
{return new WebMsg("admin or user");
}
/// <summary>
/// role=admin和user
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(Roles = "admin")]
[Authorize(Roles = "user")]
public WebMsg AdminAndUserGet()
{return new WebMsg("admin and user");
}

这里推荐使用enum枚举类型,这样不会出现拼写错误

封装好的代码

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();//反解密,获取其中的Claimsvar 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/swashbucklebuilder.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.rhkb.cn/news/279091.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:ListItemGroup)

该组件用来展示列表item分组&#xff0c;宽度默认充满List组件&#xff0c;必须配合List组件来使用。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件的父组件只能是List。 使用说明 当List…

51单片机LED8*8点阵显示坤坤跳舞打篮球画面

我们作为一名合格的 ikun&#xff0c;专业的小黑子&#xff0c;这个重要的知识必须学会。 先看效果&#xff1a; 51LED点阵_鸡你太美 这里我们首先要用到延时函数Delay&#xff1a; void Delay(unsigned int xms) {unsigned char i, j;while(xms--){ i 2;j 239;do{while (-…

【PyQt】17-日历控件

文章目录 前言一、代码二、运行结果总结 前言 固定格式的表述 日期的获取 一、代码 #Author &#xff1a;susocool #Creattime:2024/3/19 #FileName:40-日历控件 #Description: 日历控件的展示 import sys from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQ…

V-JEPA模型,非LLM另外的选择,AGI的未来:迈向Yann LeCun先进机器智能(AMI)愿景的下一步

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Redisson 分布式锁原理分析

Redisson 分布式锁原理分析 示例程序 示例程序&#xff1a; public class RedissonTest {public static void main(String[] args) {Config config new Config();config.useSingleServer().setPassword("123456").setAddress("redis://127.0.0.1:6379"…

最新Java面试题2【2024初级】

下载链接&#xff1a;博主已将以上这些面试题整理成了一个面试手册&#xff0c;是PDF版的 互联网大厂面试题 1&#xff1a;阿里巴巴Java面试题 2&#xff1a;阿里云Java面试题-实习生岗 3&#xff1a;腾讯Java面试题-高级 4&#xff1a;字节跳动Java面试题 5&#xff1a;字…

Apache Dolphinscheduler - 无需重启 Master-Server 停止疯狂刷日志解决方案

记录的是一个 3.0 比较难搞的问题&#xff0c;相信不少使用过 3.0 的用户都遇到过 Master 服务中存在一些工作流或者任务流一直不停的死循环的问题&#xff0c;导致疯狂刷日志。不过本人到现在也没找到最关键的触发原因&#xff0c;只是看到一些连锁反应带来的结果…… 影响因素…

第十届教育技术前沿国际会议(ICFET 2024)即将召开!

ICFET 2024 | Malacca, MalaysiaInstallation Documentation for your Bootstrap Templatehttp://www.ICFET.org/ 组织单位&#xff1a; 会议主题&#xff1a; 整合教育技术 社交媒体和社交网络 语义网 3.0 播客播放视频讲座 播客向学生提供反馈 Wiki 和博客在高等教育中的…

MySQL最实用面试题(2024-3-14持续更新中)

MySQL篇面试题 一、介绍 ​ 这是由小龙同学自己总结领悟的mysql面试题的解析&#xff0c;也是面试宝典 二、题目 1.数据库三大范式&#xff1a; –作用&#xff1a; ​ 使表结构清晰&#xff0c;减少数据冗余&#xff08;简单讲就是重复&#xff09;&#xff0c;提高查询…

《探索AI辅助研发的未来之路》

在当今科技飞速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐渗透到各个领域&#xff0c;其中之一便是研发领域。AI辅助研发正以惊人的速度改变着我们对于创新和发现的理解。本文将从技术进展、行业应用、挑战与机遇、未来趋势、法规影响以及人才培养等方…

STP环路避免实验(华为)

思科设备参考&#xff1a;STP环路避免实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 Spanning Tree Protocol&#xff08;STP&#xff09;&#xff0c;即生成树协议&#xff0c;是一种数据链路层协议。主要作用是防止二层环路&#xff0c;并自适应网络变化和故障…

Vue+SpringBoot打造民宿预定管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

GPT-5:人工智能的下一个前沿即将到来

当我们站在人工智能新时代的门槛上时&#xff0c;GPT-5即将到来的呼声愈发高涨且迫切。作为革命性的GPT-3的继任者&#xff0c;GPT-5承诺将在人工智能领域迈出量子跃迁式的进步&#xff0c;其能力可能重新定义我们与技术的互动方式。 通往GPT-5之路 通往GPT-5的旅程已经标记着…

鸿蒙-自定义组件的生命周期

目录 自定义组件的生命周期 1.aboutToAppear 2.aboutToDisappear 3.onPageShow 4.onPageHide 5.onBackPress 日志输出 1.显示页面 2.页面点击返回按钮 3.页面跳转 4.页面返回 自定义组件的生命周期 先来一段列子 import router from ohos.router Entry Component…

如何对医院运营管理进行3D可视化监控?推荐帆软智慧医院建设

智慧医院是运用云计算、大数据、物联网、移动互联网和人工智能等技术&#xff0c;通过建立互联、物联、感知、智能的医疗服务环境&#xff0c;整合医疗资源&#xff0c;优化医疗服务流程&#xff0c;规范诊疗行为&#xff0c;提高诊疗效率&#xff0c;辅助临床决策和医院管理决…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Text)

显示一段文本的组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含Span和ImageSpan子组件。 接口 Text(content?: string | Resource, value?: TextOptions) 从API versi…

四.排序(冒泡/选择)

目录 11-排序介绍 常见排序算法: 12-冒泡排序介绍 代码要求: 思路: 13-冒泡排序 代码: 14-选择排序 简单写法: 好的写法: 11-排序介绍 排序&#xff1a;将一组“无序”的记录序列调整为“有序”的记录序列。 列表排序&#xff1a;将无序列表变为有序列表 输入&#…

Samtec科普 | 一文了解患者护理应用连接器

【摘要/前言】 通过医疗专业人士为患者提供护理的种种需求&#xff0c;已经不限于手术室与医院的各种安全状况。当今许多患者的护理都是在其他环境进行&#xff0c;例如医生办公室、健康中心&#xff0c;还有越来越普遍的住家。尤其是需要长期看护的患者&#xff0c;所需的科技…

达梦数据库SQL

达梦JSON函数技术文档 SQL中关键词处理 -- 必须要使用双引号包裹 select id,"comment" from t_cmp_rd_process;select id,"commit" from t_cmp_rd_gjj_eva;JSON_EXTRACT函数 -- party_sup_other_json 是包含JSON数据的列名。 -- $.content_abstract 是J…

Vue.js 应用实现监控可观测性最佳实践

本文由观测云团队编写~ 前言 Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;Vue 都可以胜任。 …