0


ASP.NET Core 8.0 WebApi 从零开始学习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>privatestaticreadonlystring jwtKey ="TokenKey";publicrecordUserTest(string Name,string Account,string Password);staticvoidMain(string[] args){UserTest userTest =newUserTest("小王","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>publicstaticIJwtEncoderGetEncoder(){IJwtAlgorithm algorithm =newHMACSHA256Algorithm();//加密方式IJsonSerializer serializer =newJsonNetSerializer();//序列化JsonIBase64UrlEncoder urlEncoder =newJwtBase64UrlEncoder();//base64加解密IJwtEncoder encoder =newJwtEncoder(algorithm, serializer, urlEncoder);//JWT编码return encoder;}/// <summary>/// 获取解密密钥/// </summary>/// <returns></returns>publicstaticIJwtDecoderGetDecoder(){IJsonSerializer serializer =newJsonNetSerializer();IDateTimeProvider provider =newUtcDateTimeProvider();IJwtValidator validator =newJwtValidator(serializer, provider);IBase64UrlEncoder urlEncoder =newJwtBase64UrlEncoder();IJwtAlgorithm algorithm =newHMACSHA256Algorithm();IJwtDecoder decoder =newJwtDecoder(serializer, validator, urlEncoder, algorithm);return decoder;}

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

在WebApi中简单使用

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

publicclassUserJwtLogin{/// <summary>/// 用户Id,因为数据库的Id是唯一的,不会重复/// </summary>publiclong UserId {get;set;}/// <summary>/// 过期时间/// </summary>publicDateTime ExpireTime {get;set;}}

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

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

我这里是这么写的

新建一个JwtHelper

publicstaticclassJwtHelper{privatestaticreadonlystring JwtKey ="这里填你的密钥";/// <summary>/// 获取加密解密/// </summary>/// <returns></returns>privatestaticIJwtEncoderGetEncoder(){IJwtAlgorithm algorithm =newHMACSHA256Algorithm();//加密方式IJsonSerializer serializer =newJsonNetSerializer();//序列化JsonIBase64UrlEncoder urlEncoder =newJwtBase64UrlEncoder();//base64加解密IJwtEncoder encoder =newJwtEncoder(algorithm, serializer, urlEncoder);//JWT编码return encoder;}/// <summary>/// 获取解密密钥/// </summary>/// <returns></returns>privatestaticIJwtDecoderGetDecoder(){IJsonSerializer serializer =newJsonNetSerializer();IDateTimeProvider provider =newUtcDateTimeProvider();IJwtValidator validator =newJwtValidator(serializer, provider);IBase64UrlEncoder urlEncoder =newJwtBase64UrlEncoder();IJwtAlgorithm algorithm =newHMACSHA256Algorithm();IJwtDecoder decoder =newJwtDecoder(serializer, validator, urlEncoder, algorithm);return decoder;}/// <summary>/// 加密/// </summary>publicstaticstringEncode(object payload){var encoder =GetEncoder();var token = encoder.Encode(payload, JwtKey);return token;}/// <summary>/// 解密/// </summary>publicstaticTDecode<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>publicstaticstringDecode(string token){var decoder =GetDecoder();var data = decoder.Decode(token, JwtKey);return data;}}

两个实体类

publicclassUserJwtLogin{/// <summary>/// 用户Id,因为数据库的Id是唯一的,不会重复/// </summary>publiclong UserId {get;set;}/// <summary>/// 过期时间/// </summary>publicDateTime ExpireTime {get;set;}}
publicclassWebMsg{publicint Code {get;set;}=200;publicbool Success {get;set;}=true;publicobject Data {get;set;}publicstring Msg {get;set;}="操作成功!";publicWebMsg(){}publicWebMsg(object data){
        Data = data;}}

简单使用

/// <summary>/// Jwt登录/// </summary>[Route("api/[controller]/[action]")][ApiController]publicclassLoginController:ControllerBase{//这个是我的Nlog配置,这里不做展开privateNlogService nlogService;publicLoginController(NlogService nlogService){this.nlogService = nlogService;}/// <summary>/// JWT登录/// </summary>/// <param name="username">账号</param>/// <param name="password">密码</param>/// <returns></returns>[HttpGet]publicWebMsgLogin(string username,string password){if(username ==null|| password ==null){returnnewWebMsg(){
                Msg ="登录信息为空",
                Success =false,};}if(username =="admin"&& password =="123456"){//这里是模拟拿到数据库的User表的Idvar pyload =newUserJwtLogin(){
                UserId =291,
                ExpireTime = DateTime.Now.AddDays(1)};var token = JwtHelper.Encode(pyload);returnnewWebMsg(token);}else{returnnewWebMsg(){
                Msg ="登录失败,账号或者密码错误",
                Success =false,};}}/// <summary>/// Jwt解密/// </summary>/// <param name="token"></param>/// <returns></returns>[HttpGet]publicWebMsgDecode(string token){try{var res = JwtHelper.Decode(token);returnnewWebMsg(res);}catch(Exception ex){
            nlogService.Error("登录解析失败:"+ token);
            nlogService.Error(ex.Message);returnnewWebMsg(){ 
                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里面的代码实在是太多了,这里我们分开一下

usingMicrosoft.OpenApi.Models;usingSystem.Reflection;namespaceJtwTestWebApi{publicclassProgram{publicstaticvoidMain(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>publicstaticvoidAddMyService(WebApplicationBuilder builder){
            builder.Services.AddCors(options =>{
                options.AddPolicy("MyPolicy", policy =>{
                    policy.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod();});});
            builder.Services.AddSwaggerGen(options =>{
                options.SwaggerDoc("v1",newOpenApiInfo{
                    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

publicclassWebMsg{publicobject Data {get;set;}publicbool Success {get;set;}=true;publicstring Msg {get;set;}="操作成功!";publicWebMsg(){}publicWebMsg(object data){
        Data = data;}}
/// <summary>/// 测试控制器/// </summary>[Route("api/[controller]/[action]")][ApiController]publicclassTestController:ControllerBase{/// <summary>/// 测试返回值/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetTest(){returnnewWebMsg("测试返回值");}}

在这里插入图片描述

最简单的Jwt认证

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

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

获取JwtConfig

我们在appsetting.json里面添加

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

在这里插入图片描述

publicclassJwtConfig{/// <summary>/// 密钥/// </summary>publicstring SecretKey {get;set;}/// <summary>/// 发布者/// </summary>publicstring Issuer {get;set;}/// <summary>/// 接受者/// </summary>publicstring Audience {get;set;}/// <summary>/// 过期时间(min)/// </summary>publicint Expired {get;set;}}

获取Config

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

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

新建JwtHelper类

publicclassJwtHelper{publicJwtConfig JwtConfig {get;set;}publicJwtHelper(){}/// <summary>/// 添加Jwt服务/// </summary>publicvoidAddJwtService(){}/// <summary>/// 返回/// </summary>/// <returns></returns>publicstringGetJwtToken(){//简单测试一下var token = JsonConvert.SerializeObject(JwtConfig);return token;}}

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

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

在这里插入图片描述

然后在控制器里面获取Token

/// <summary>/// 测试控制器/// </summary>[Route("api/[controller]/[action]")][ApiController]publicclassTestController:ControllerBase{privateJwtHelper jwtHelper;/// <summary>/// 通过Ioc得到Jwt/// </summary>/// <param name="jwtHelper"></param>publicTestController(JwtHelper jwtHelper){this.jwtHelper = jwtHelper;}/// <summary>/// 测试返回值/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetTest(){returnnewWebMsg("测试返回值");}/// <summary>/// 获取JwtToken/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetJwtToken(){var token = jwtHelper.GetJwtToken();returnnewWebMsg(token);}}

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

完善JwtConfig

usingMicrosoft.IdentityModel.Tokens;usingSystem.Text;namespaceJtwTestWebApi.Models{publicclassJwtConfig{/// <summary>/// 密钥/// </summary>publicstring SecretKey {get;set;}/// <summary>/// 发布者/// </summary>publicstring Issuer {get;set;}/// <summary>/// 接受者/// </summary>publicstring Audience {get;set;}/// <summary>/// 过期时间(min)/// </summary>publicint Expired {get;set;}/// <summary>/// 生效时间/// </summary>publicDateTime NotBefore => DateTime.Now;/// <summary>/// 过期时间/// </summary>publicDateTime Expiration => DateTime.Now.AddMinutes(Expired);/// <summary>/// 密钥Bytes/// </summary>privateSecurityKey SigningKey =>newSymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));/// <summary>/// 加密后的密钥,使用HmacSha256加密/// </summary>publicSigningCredentials SigningCredentials =>newSigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);}}

在JwtHelper里面使用

/// <summary>/// 最简单的JwtToken/// </summary>/// <returns></returns>publicstringGetJwtToken(){var claims =newList<Claim>();var jwtSecurityToken =newJwtSecurityToken(
            JwtConfig.Issuer,
            JwtConfig.Audience,
            claims,
            JwtConfig.NotBefore,
            JwtConfig.Expiration,
            JwtConfig.SigningCredentials
        );var token =newJwtSecurityTokenHandler().WriteToken( jwtSecurityToken );return token;}

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

在这里插入图片描述

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MTAzODUyODgsImV4cCI6MTcxMDM4NzA4OCwiaXNzIjoiWGlhb1dhbmciLCJhdWQiOiJYaWFvV2FuZyJ9.v7UbQOba7VoNgoiRsoIQkFJKQTFBMLJlYEKBIfdFV4o

授权

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

在JwtConfig里面添加对应的授权

publicclassJwtConfig{/// <summary>/// 密钥/// </summary>publicstring SecretKey {get;set;}/// <summary>/// 发布者/// </summary>publicstring Issuer {get;set;}/// <summary>/// 接受者/// </summary>publicstring Audience {get;set;}/// <summary>/// 过期时间(min)/// </summary>publicint Expired {get;set;}/// <summary>/// 生效时间/// </summary>publicDateTime NotBefore => DateTime.Now;/// <summary>/// 过期时间/// </summary>publicDateTime Expiration => DateTime.Now.AddMinutes(Expired);/// <summary>/// 密钥Bytes/// </summary>privateSecurityKey SigningKey =>newSymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));/// <summary>/// 加密后的密钥,使用HmacSha256加密/// </summary>publicSigningCredentials SigningCredentials =>newSigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);/// <summary>/// 认证用的密钥/// </summary>publicSymmetricSecurityKey SymmetricSecurityKey =>newSymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));}

在这里插入图片描述

在JwtHelper中

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

调用JwtHelper

var jwtConfig =newJwtConfig();
  builder.Configuration.Bind("JwtConfig",jwtConfig);var jwtHelper =newJwtHelper(){
      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

usingJtwTestWebApi.Models;usingJtwTestWebApi.Utils;usingMicrosoft.AspNetCore.Authorization;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Mvc;namespaceJtwTestWebApi.Controllers{/// <summary>/// 测试控制器/// </summary>[Route("api/[controller]/[action]")][ApiController]publicclassTestController:ControllerBase{privateJwtHelper jwtHelper;/// <summary>/// 通过Ioc得到Jwt/// </summary>/// <param name="jwtHelper"></param>publicTestController(JwtHelper jwtHelper){this.jwtHelper = jwtHelper;}/// <summary>/// 测试返回值/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetTest(){returnnewWebMsg("测试返回值");}/// <summary>/// 获取JwtToken/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetJwtToken(){var token = jwtHelper.GetJwtToken();returnnewWebMsg(token);}/// <summary>/// 可以在方法前面加Authorize/// </summary>/// <returns></returns>[Authorize][HttpGet]publicWebMsgGetByJwt(){returnnewWebMsg("Jwt测试成功!");}}}

JtwTestWebApi

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

usingJtwTestWebApi.Models;usingMicrosoft.AspNetCore.Authorization;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Mvc;namespaceJtwTestWebApi.Controllers{/// <summary>/// Jwt测试类/// </summary>[Route("api/[controller]/[action]")][ApiController][Authorize]publicclassJwtTestController:ControllerBase{/// <summary>/// 不需要Jwt/// </summary>/// <returns></returns>[AllowAnonymous][HttpGet]publicWebMsgNoJwtGet(){returnnewWebMsg("我不需要Jwt");}/// <summary>/// 需要Jwt/// </summary>/// <returns></returns>[HttpGet]publicWebMsgJwtGet(){returnnewWebMsg("我需要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>publicstaticvoidSwaggerAddJwtHeader(SwaggerGenOptions options){
    options.AddSecurityRequirement(newOpenApiSecurityRequirement(){{newOpenApiSecurityScheme{
                    Reference =newOpenApiReference{
                        Type = ReferenceType.SecurityScheme,
                        Id ="Bearer",}},newstring[]{}}});
    options.AddSecurityDefinition("Bearer",newOpenApiSecurityScheme{
        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>publicIEnumerable<Claim>Decode(HttpRequest request){var authorization = request.Headers["Authorization"].ToString();//因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头var auth = authorization.Split(" ")[1];var handler =newJwtSecurityTokenHandler();//反解密,获取其中的Claimsvar payload = handler.ReadJwtToken(auth).Payload;var claims = payload.Claims;return claims;}

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

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

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

简单封装一下

usingJtwTestWebApi.Models;usingMicrosoft.AspNetCore.Authentication.JwtBearer;usingMicrosoft.IdentityModel.Tokens;usingMicrosoft.OpenApi.Models;usingNewtonsoft.Json;usingSwashbuckle.AspNetCore.SwaggerGen;usingSystem.IdentityModel.Tokens.Jwt;usingSystem.Security.Claims;usingSystem.Text;namespaceJtwTestWebApi.Utils{publicclassJwtHelper{publicJwtConfig JwtConfig {get;set;}publicJwtHelper(){}/// <summary>/// 添加Jwt服务/// </summary>publicvoidAddJwtService(IServiceCollection services){
            services.AddAuthentication(option =>{//认证middleware配置
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{
                options.TokenValidationParameters =newTokenValidationParameters{//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>publicstringGetJwtToken(){var claims =newList<Claim>();var jwtSecurityToken =newJwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );var token =newJwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token ="Bearer "+ token;return token;}/// <summary>/// 添加claims信息/// </summary>/// <param name="claims"></param>/// <returns></returns>publicstringGetJwtToken(List<Claim> claims){var jwtSecurityToken =newJwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );var token =newJwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token ="Bearer "+ token;return token;}/// <summary>/// UserModel类Token/// </summary>/// <param name="user"></param>/// <returns></returns>publicstringGetJwtToken(JwtUserModel user){var claims =newList<Claim>(){newClaim("UserId",user.UserId.ToString()),newClaim("UserName",user.UserName),newClaim("UserType",user.UserType.ToString()),};returnGetJwtToken(claims);}/// <summary>/// Swagger添加Jwt功能/// </summary>/// <param name="options"></param>publicstaticvoidSwaggerAddJwtHeader(SwaggerGenOptions options){
            options.AddSecurityRequirement(newOpenApiSecurityRequirement(){{newOpenApiSecurityScheme{
                            Reference =newOpenApiReference{
                                Type = ReferenceType.SecurityScheme,
                                Id ="Bearer",}},newstring[]{}}});
            options.AddSecurityDefinition("Bearer",newOpenApiSecurityScheme{
                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>publicIEnumerable<Claim>Decode(HttpRequest request){var authorization = request.Headers["Authorization"].ToString();//因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头var auth = authorization.Split(" ")[1];var handler =newJwtSecurityTokenHandler();//反解密,获取其中的Claimsvar payload = handler.ReadJwtToken(auth).Payload;var claims = payload.Claims;return claims;}/// <summary>/// 解析得到User/// </summary>/// <param name="request"></param>/// <returns></returns>publicJwtUserModelDecodeToUser(HttpRequest request){var claims =Decode(request);var user =newJwtUserModel(){
                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]publicWebMsgGetJwtToken3(){var token = jwtHelper.GetJwtToken(newJwtUserModel(){
        UserName ="小王",
        UserId ="32",
        UserType ="admin"});returnnewWebMsg(token);}[Authorize][HttpGet]publicWebMsgGetByJwt3(){var dic = jwtHelper.DecodeToUser(this.Request);returnnewWebMsg("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]publicWebMsgGetJwtToken_Admin(){var token = jwtHelper.GetJwtToken(newList<Claim>(){newClaim(ClaimTypes.Role,"admin")});returnnewWebMsg(token);}/// <summary>/// 获取Role = user/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetJwtToken_User(){var token = jwtHelper.GetJwtToken(newList<Claim>(){newClaim(ClaimTypes.Role,"user")});returnnewWebMsg(token);}/// <summary>/// 获取Role = admin和user/// </summary>/// <returns></returns>[HttpGet]publicWebMsgGetJwtToken_UserAndAdmin(){var token = jwtHelper.GetJwtToken(newList<Claim>(){newClaim(ClaimTypes.Role,"user"),newClaim(ClaimTypes.Role,"admin")});returnnewWebMsg(token);}
不同权限的jwt认证
/// <summary>/// 需要role=admin/// </summary>/// <returns></returns>[HttpGet][Authorize(Roles ="admin")]publicWebMsgAdminGet(){returnnewWebMsg("admin");}/// <summary>/// role=user/// </summary>/// <returns></returns>[HttpGet][Authorize(Roles ="user")]publicWebMsgUserGet(){returnnewWebMsg("user");}/// <summary>/// role = admin或user/// </summary>/// <returns></returns>[HttpGet][Authorize(Roles ="admin,user")]publicWebMsgAdminOrUserGet(){returnnewWebMsg("admin or user");}/// <summary>/// role=admin和user/// </summary>/// <returns></returns>[HttpGet][Authorize(Roles ="admin")][Authorize(Roles ="user")]publicWebMsgAdminAndUserGet(){returnnewWebMsg("admin and user");}

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

封装好的代码

usingMicrosoft.IdentityModel.Tokens;usingSystem.Text;namespaceJtwTestWebApi.Models{publicclassJwtConfig{/// <summary>/// 密钥/// </summary>publicstring SecretKey {get;set;}/// <summary>/// 发布者/// </summary>publicstring Issuer {get;set;}/// <summary>/// 接受者/// </summary>publicstring Audience {get;set;}/// <summary>/// 过期时间(min)/// </summary>publicint Expired {get;set;}/// <summary>/// 生效时间/// </summary>publicDateTime NotBefore => DateTime.Now;/// <summary>/// 过期时间/// </summary>publicDateTime Expiration => DateTime.Now.AddMinutes(Expired);/// <summary>/// 密钥Bytes/// </summary>privateSecurityKey SigningKey =>newSymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));/// <summary>/// 加密后的密钥,使用HmacSha256加密/// </summary>publicSigningCredentials SigningCredentials =>newSigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256);/// <summary>/// 认证用的密钥/// </summary>publicSymmetricSecurityKey SymmetricSecurityKey =>newSymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));}}
usingJtwTestWebApi.Models;usingMicrosoft.AspNetCore.Authentication.JwtBearer;usingMicrosoft.IdentityModel.Tokens;usingMicrosoft.OpenApi.Models;usingNewtonsoft.Json;usingSwashbuckle.AspNetCore.SwaggerGen;usingSystem.IdentityModel.Tokens.Jwt;usingSystem.Security.Claims;usingSystem.Text;namespaceJtwTestWebApi.Utils{publicclassJwtHelper{publicJwtConfig JwtConfig {get;set;}publicJwtHelper(){}/// <summary>/// 添加Jwt服务/// </summary>publicvoidAddJwtService(IServiceCollection services){
            services.AddAuthentication(option =>{//认证middleware配置
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{
                options.TokenValidationParameters =newTokenValidationParameters{//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>publicstringGetJwtToken(){var claims =newList<Claim>();var jwtSecurityToken =newJwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );var token =newJwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token ="Bearer "+ token;return token;}/// <summary>/// 添加claims信息/// </summary>/// <param name="claims"></param>/// <returns></returns>publicstringGetJwtToken(List<Claim> claims){var jwtSecurityToken =newJwtSecurityToken(
                    JwtConfig.Issuer,
                    JwtConfig.Audience,
                    claims,
                    JwtConfig.NotBefore,
                    JwtConfig.Expiration,
                    JwtConfig.SigningCredentials
                );var token =newJwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
            token ="Bearer "+ token;return token;}/// <summary>/// UserModel类Token/// </summary>/// <param name="user"></param>/// <returns></returns>publicstringGetJwtToken(JwtUserModel user){var claims =newList<Claim>(){newClaim("UserId",user.UserId.ToString()),newClaim("UserName",user.UserName),newClaim("UserType",user.UserType.ToString()),};returnGetJwtToken(claims);}/// <summary>/// Swagger添加Jwt功能/// </summary>/// <param name="options"></param>publicstaticvoidSwaggerAddJwtHeader(SwaggerGenOptions options){
            options.AddSecurityRequirement(newOpenApiSecurityRequirement(){{newOpenApiSecurityScheme{
                            Reference =newOpenApiReference{
                                Type = ReferenceType.SecurityScheme,
                                Id ="Bearer",}},newstring[]{}}});
            options.AddSecurityDefinition("Bearer",newOpenApiSecurityScheme{
                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>publicIEnumerable<Claim>Decode(HttpRequest request){var authorization = request.Headers["Authorization"].ToString();//因为我们的Jwt是自带【Bearer 】这个请求头的,所以去掉前面的头var auth = authorization.Split(" ")[1];var handler =newJwtSecurityTokenHandler();//反解密,获取其中的Claimsvar payload = handler.ReadJwtToken(auth).Payload;var claims = payload.Claims;return claims;}/// <summary>/// 解析得到User/// </summary>/// <param name="request"></param>/// <returns></returns>publicJwtUserModelDecodeToUser(HttpRequest request){var claims =Decode(request);var user =newJwtUserModel(){
                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;}}}
namespaceJtwTestWebApi.Models{publicclassJwtUserModel{publicstring UserId {get;set;}publicstring UserName {get;set;}publicstring UserType {get;set;}}}
usingJtwTestWebApi.Models;usingJtwTestWebApi.Utils;usingMicrosoft.Extensions.Configuration;usingMicrosoft.OpenApi.Models;usingSystem.IdentityModel.Tokens.Jwt;usingSystem.Reflection;namespaceJtwTestWebApi{publicclassProgram{publicstaticvoidMain(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>publicstaticvoidAddMyService(WebApplicationBuilder builder){#region 默认的Webapi配置
            builder.Services.AddCors(options =>{
                options.AddPolicy("MyPolicy", policy =>{
                    policy.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod();});});
            builder.Services.AddSwaggerGen(options =>{
                options.SwaggerDoc("v1",newOpenApiInfo{
                    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 =newJwtConfig();
            builder.Configuration.Bind("JwtConfig",jwtConfig);var jwtHelper =newJwtHelper(){
                JwtConfig = jwtConfig
            };//将JwtHelper添加到Services里面
            builder.Services.AddSingleton<JwtHelper>(jwtHelper);
            jwtHelper.AddJwtService(builder.Services);#endregion}}}

appsettings.json中添加

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

总结

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

标签: asp.net 学习 后端

本文转载自: https://blog.csdn.net/qq_44695769/article/details/136658078
版权归原作者 龙中舞王 所有, 如有侵权,请联系我们删除。

“ASP.NET Core 8.0 WebApi 从零开始学习JWT登录认证”的评论:

还没有评论