.Net Core webapi 实现JWT认证

news2025/1/19 2:22:10

文章目录

    • 需求
    • 准备
    • 创建JWT配置
    • 创建JWTService
    • 注册JWT
    • 创建中间件读取jwt的token
    • 在需要的接口上添加属性
    • 启动认证
    • 启动swagger的授权认证
    • 使用

需求

实现一个记录某个用户所有操作的功能

准备

  1. 创建你的webapi项目
  2. 从nuget下载安装JWT资源包根据你的项目使用.net版本下载对应的jwt版本,测试项目使用了.net8.0:
    Microsoft.AspNetCore.Authentication.JwtBearer
    在这里插入图片描述

创建JWT配置

在appsettings.json中新增JWTOptions

"JWTOptions": {
  //你的jwt加密密钥
  "SecretKey": "ThisIsASecretKeyForJWTTokenGeneration",
  "Issuer": "localhost", //令牌颁发者
  "Audience": "localhost", //令牌接收者
  "Expired": 5 //令牌过期时间
}

创建jwt配置类并注册

public class JWTOptions
{
    /// <summary>
    /// jwt加密密钥,任意字符串
    /// </summary>
    public string SecretKey { get; set; }

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

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

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

//在program.cs中注册该option
builder.Services.Configure<JWTOptions>(builder.Configuration.GetSection("JWTOptions"));

创建JWTService

创建服务生成token

public class GenerateJWTService
{

    private readonly JWTOptions _options;

    public GenerateJWTService(IOptionsMonitor<JWTOptions> options)
    {
        _options = options.CurrentValue;
    }

    public JWTokenTResult GenerateToken(string userName)
    {
        var claims = new List<Claim> 
        {
            new("userName", userName),
            new(JwtRegisteredClaimNames.Sub, userName),
        };

        var validFrom = DateTime.Now;
        var validTo = DateTime.Now.AddMinutes(10);
        //创建令牌
        var jwt = new JwtSecurityToken(
            issuer: _options.Issuer,
            audience: _options.Audience,
            claims: claims,
            notBefore: validFrom,
            expires: validTo,
            signingCredentials: new Microsoft.IdentityModel.Tokens.SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.SecretKey)), SecurityAlgorithms.HmacSha256)
            ) ;

        string accessToken = new JwtSecurityTokenHandler().WriteToken(jwt);

        return new JWTokenTResult
        {
            AccessToken = accessToken,
            ExpireIn = _options.Expired * 60,
            TokenType = JwtBearerDefaults.AuthenticationScheme
        };
    }
}

//jwt模型
public class JWTokenTResult
{
    public string AccessToken {  get; set; }

    public string RefreshToken {  get; set; }

    /// <summary>
    /// 过期时间,单位s
    /// </summary>
    public int ExpireIn {  get; set; }

    public string TokenType {  get; set; }

    public LoginUserModel User { get; set; }
}

public class LoginUserModel
{
    public string UserId { get; set; }

    public string UserName { get; set; }

    public string Roles {  get; set; }
}

注册JWT

把jwt注册到服务中

public static void AddJWTTokenAuth(this IServiceCollection serivces, IConfiguration configuration)
{
    var jwtSettings = configuration.GetSection("JWTOptions");
    serivces.Configure<JWTOptions>(configuration.GetSection("JWTOptions"));
    serivces.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
    {
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,//启动token有效时间校验
            ClockSkew = TimeSpan.Zero, //默认ClockSkew是5分钟,当前时间和JWT的过期时间之间的差距小于 5 分钟,Token 仍然会被认为是有效的
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtSettings["Issuer"],
            ValidAudience = jwtSettings["Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]))
        };

    });
}

//在program.cs中调用
builder.Services.AddJWTTokenAuth(builder.Configuration);

创建中间件读取jwt的token

using System.IdentityModel.Tokens.Jwt;
using System.Text;

namespace JWTTest
{
    public class UserMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly GenerateJWTService _generateJWTService;
        public UserMiddleware(RequestDelegate next, GenerateJWTService generateJWTService)
        {
            _next = next;
            _generateJWTService = generateJWTService;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            if(context.User.Identity.IsAuthenticated)
            {
                var requestUrl = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}";

                if(context.Request.Method.ToUpper() == "GET")
                {

                }
                else
                {
                    context.Request.EnableBuffering();// 启用请求体流缓冲,以便多次读取

                    var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
                    var body = await reader.ReadToEndAsync();

                    // 将请求体位置重置,避免后续中间件或控制器读取不到
                    context.Request.Body.Position = 0;
                }
                var userName = context.User.Claims.FirstOrDefault(c => c.Type == "userName")?.Value;

                Console.WriteLine($"{userName} request");


                var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
                var tokenhandler = new JwtSecurityTokenHandler();

                var jwtToken = tokenhandler.ReadToken(token) as JwtSecurityToken;
                if(jwtToken != null)
                {
                	//如果token将要过期,实现用户无感刷新,只需要前端判断response的header中是否有New-Access-Token,有就替换原来的token
                    if(jwtToken.ValidTo - DateTime.UtcNow < TimeSpan.FromMinutes(5))
                    {
                        var newAccessToken = _generateJWTService.GenerateToken(userName);
                        context.Response.Headers["New-Access-Token"] = "";//newAccessToken;
                    }
                }
            }

            await _next(context);
        }
    }
}

在需要的接口上添加属性

在需要的接口上添加[Authorize]属性,也可以加到controller上,然后给不需要认证的接口添加[AllowAnonymous],跳过认证

using JWTTest.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JWTTest.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;
        private readonly GenerateJWTService _generateJWTService;

        public WeatherForecastController(ILogger<WeatherForecastController> logger, GenerateJWTService generateJWTService)
        {
            _logger = logger;
            _generateJWTService = generateJWTService;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        [Authorize]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }

        [Authorize]
        [HttpPost("test/testpost")]
        public ActionResult Test(LoginUserModel loginUserModel)
        {
            return default;
        }
        [HttpGet("/login")]
        public ActionResult GetLogin(string name, string password)
        {
            var jwtTokenResult = _generateJWTService.GenerateToken(name);
            //jwtTokenResult.refresh_token = refreshToken;
            return Ok(jwtTokenResult);//这里可按需返回   如果不想返回用户信息  比如密码  可以在_generateJwt.GenerateEncodedTokenAsync去掉哦
        }
    }
}

启动认证

//在program.cs中启动认证,顺序不能错
app.UseAuthentication(); //身份验证,验证令牌信息
app.UseAuthorization();//授权

启动swagger的授权认证

using Microsoft.OpenApi.Models;

namespace Wonder.OHTC.Backend.Extension
{
    public static class SwaggerAuthExtension
    {
        /// <summary>
        /// 为swagger添加authorization
        /// </summary>
        /// <param name="services"></param>
        public static void AddSwaggerExtension(this IServiceCollection services)
        {
            services.AddSwaggerGen(options =>
            {
                // 为 Swagger JSON and UI设置xml文档注释路径
                var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径)
                var xmlPath = Path.Combine(basePath, "Wonder.OHTC.Backend.xml");
                // 添加控制器层注释,true表示显示控制器注释 false表示只显示API接口的注释
                options.IncludeXmlComments(xmlPath, true);

                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                {
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Description = "Please enter JWT with bearer into field",
                    Name = "Authorization",//jwt默认的参数名称
                    Type = SecuritySchemeType.ApiKey
                });
                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        Array.Empty<string>()
                    }


                });
            });
        }
    }
}

//在program.cs中调用
builder.Services.AddSwaggerExtension();

也可以直接在program里面直接注册jwt


using JWTTest.Model;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;

namespace JWTTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // 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(options =>
            {
                options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme()
                {
                    In = Microsoft.OpenApi.Models.ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Description = "Please enter JWT with bearer into field",
                    Name = "Authorization",//jwt默认的参数名称
                    Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
                });
                options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                            
                        },
                        new string[]{}
                    }
                    
                
                });
            });
            var jwtSettings = builder.Configuration.GetSection("JWTOptions");
            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,//启动token有效时间校验
                    ClockSkew = TimeSpan.Zero, //默认ClockSkew是5分钟,当前时间和JWT的过期时间之间的差距小于 5 分钟,Token 仍然会被认为是有效的
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = jwtSettings["Issuer"],
                    ValidAudience = jwtSettings["Audience"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]))
                };

            });
            builder.Services.Configure<JWTOptions>(builder.Configuration.GetSection("JWTOptions"));
            builder.Services.AddSingleton<GenerateJWTService>();
            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();

            app.UseAuthentication(); //身份验证
            app.UseAuthorization();//授权

            app.MapControllers();

            app.UseMiddleware<UserMiddleware>();
            app.Run();
        }
    }
}

使用

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux《Linux简介与环境的搭建》

在学习了C或者是C语言的基础知识之后就可以开始Linux的学习了&#xff0c;现在Linux无论是在服务器领域还是在桌面领域都被广泛的使用&#xff0c;所以Linxu也是我们学习编程的重要环节&#xff0c;在此接下来我们将会花大量的时间在Linxu的学习上。在学习Linux初期你可以会像初…

从零开始:Gitee 仓库创建与 Git 配置指南

引言 Git 是一款广泛使用的版本控制工具&#xff0c;它能够帮助开发者在开发过程中高效地管理代码的版本。而 Gitee&#xff08;码云&#xff09;是国内知名的 Git 托管平台&#xff0c;它提供了强大的代码托管、团队协作和项目管理功能。如果你是 Git 和 Gitee 的新手&#x…

创建模式、结构模式及行为模式

谁在什么地方提供什么功能&#xff1f; 要设计几个类?这些类各个是什么功能&#xff1f;相互间的关系是什么&#xff1f; 创建模式指的是对象那么多&#xff0c;怎么把它"生"出来&#xff1f;生几个&#xff1f;从这个角度上来说数组就是一种另类的创建模式。主要…

SpringBoot链接Kafka

一、SpringBoot生产者 &#xff08;1&#xff09;修改SpringBoot核心配置文件application.propeties, 添加生产者相关信息 # 连接 Kafka 集群 spring.kafka.bootstrap-servers192.168.134.47:9093# SASL_PLAINTEXT 和 SCRAM-SHA-512 认证配置 spring.kafka.properties.securi…

Linux下源码编译安装Nginx1.24及服务脚本实战

1、下载Nginx [rootlocalhost ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz2、解压 [rootlocalhost ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/3、安装依赖 [rootlocalhost ~]# yum install gcc gcc-c make pcre-devel openssl-devel -y4、 准备 N…

解答二重积分

什么是积分&#xff1f; 一元函数的积分。具体计算过程&#xff0c;是将无数个小矩形加起来&#xff0c;然后求极限。 而今天我们要讲的积分&#xff0c;是二元函数的积分。我们可以用曲顶柱体的体积来理解。 什么是曲顶柱体&#xff1f; 它的底是xoy平面上的一个闭区域。顶是…

代理模式实现

一、概念&#xff1a;代理模式属于结构型设计模式。客户端不能直接访问一个对象&#xff0c;可以通过代理的第三者来间接访问该对象&#xff0c;代理对象控制着对于原对象的访问&#xff0c;并允许在客户端访问对象的前后进行一些扩展和处理&#xff1b;这种设置模式称为代理模…

回归预测 | MATLAB实TCN时间卷积神经网络多输入单输出回归预测

效果一览 基本介绍 回归预测 | MATLAB实TCN时间卷积神经网络多输入单输出回归预测 …………训练集误差指标………… 1.均方差(MSE)&#xff1a;166116.6814 2.根均方差(RMSE)&#xff1a;407.5741 3.平均绝对误差&#xff08;MAE&#xff09;&#xff1a;302.5888 4.平均相对…

《目标检测数据集下载地址》

一、引言 在计算机视觉的广袤领域中&#xff0c;目标检测宛如一颗璀璨的明星&#xff0c;占据着举足轻重的地位。它宛如赋予计算机一双锐利的 “眼睛”&#xff0c;使其能够精准识别图像或视频中的各类目标&#xff0c;并确定其位置&#xff0c;以边界框的形式清晰呈现。这项技…

Android系统定制APP开发_如何对应用进行系统签名

前言 当项目开发需要使用系统级别权限或frame层某些api时&#xff0c;普通应用是无法使用的&#xff0c;需要在AndroidManifest中配置sharedUserId&#xff1a; AndroidManifest.xml中的android:sharedUserId“android.uid.system”&#xff0c;代表的意思是和系统相同的uid&a…

【NextJS】PostgreSQL 遇上 Prisma ORM

NextJS 数据库 之 遇上Prisma ORM 前言一、环境要求二、概念介绍1、Prisma Schema Language&#xff08;PSL&#xff09; 结构描述语言1.1 概念1.2 组成1.2.1 Data Source 数据源1.2.2 Generators 生成器1.2.3 Data Model Definition 数据模型定义字段(数据)类型和约束关系&…

Mybatis 进阶 / Mybatis—Puls (详细)

目录 一.动态SQL 1.1标签 1.2 标签 1.3标签 1.4标签 1.5标签 1.6标签 mybatis总结&#xff1a; 二.Mybatis-Puls 2.1准备工作 2.2CRUD单元测试 2.2.1创建UserInfo实体类 2.2.2编写Mapper接⼝类 2.2.3 测试类 2.3 常见注解 2.3.1TableName 2.3.2TableField 2.4打印日…

Java工具包:高效开发的魔法钥匙

目录 一、引言 二、Hutool 工具包初体验 2.1 快速入门 2.2 常用工具类及方法详解 2.2.1 Convert 类型转换工具类 2.2.2 DateUtil 日期时间工具类 2.2.3 StrUtil 字符串工具类 2.2.4 其他常用工具类 三、其他 Java 常用工具包巡礼 3.1 Apache Commons 系列 3.2 Google…

Formality:参考设计/实现设计以及顶层设计

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482​​​ Formality存在两个重要的概念&#xff1a;参考设计/实现设计和顶层设计&#xff0c;本文就将对此进行详细阐述。参考设计/实现设计是中两个重要的全局概念&am…

HBase实训:纸币冠字号查询任务

一、实验目的 1. 理解分布式数据存储系统HBase的架构和工作原理。 2. 掌握HBase表的设计原则&#xff0c;能够根据实际业务需求设计合理的表结构。 3. 学习使用HBase Java API进行数据的插入、查询和管理。 4. 实践分布式数据存储系统在大数据环境下的应用&#xff0c;…

C#轻松实现条形码二维码生成及识别

一、前言 大家好&#xff01;我是付工。 今天给大家分享一下&#xff0c;如何基于C#来生成并识别条形码或者二维码。 二、ZXing.Net 实现二维码生成的库有很多&#xff0c;我们这里采用的是http://ZXing.Net。 ZXing是一个开放源码的&#xff0c;用Java实现的多种格式的一…

重拾Python学习,先从把python删除开始。。。

自己折腾就是不行啊&#xff0c;屡战屡败&#xff0c;最近终于找到前辈教我 第一步 删除Python 先把前阵子折腾的WSL和VScode删掉。还是得用spyder&#xff0c;跟matlab最像&#xff0c;也最容易入手。 从VScode上搞python&#xff0c;最后安装到appdata上&#xff0c;安装插…

ASP.NET Core - 依赖注入(三)

ASP.NET Core - 依赖注入&#xff08;三&#xff09; 4. 容器中的服务创建与释放 4. 容器中的服务创建与释放 我们使用了 IoC 容器之后&#xff0c;服务实例的创建和销毁的工作就交给了容器去处理&#xff0c;前面也讲到了服务的生命周期&#xff0c;那三种生命周期中对象的创…

高通8255 Android STR 启动失败要因分析调查

目录 背景&#xff1a; 调查过程&#xff1a; 步骤1&#xff1a; slog2info | grep vmm_service 步骤2&#xff1a; slog2info | grep qvm 总结&#xff1a; 解决方案 背景&#xff1a; 调试高通8255 STR的STR过程中发现Android和QNX进入STR状态后&#xff0c;脱出STR时…

Linux操作命令之云计算基础命令

一、图形化界面/文本模式 ctrlaltF2-6 图形切换到文本 ctrlalt 鼠标跳出虚拟机 ctrlaltF1 文本切换到图形 shift ctrl "" 扩大 ctrl "-" 缩小 shift ctrl "n" 新终端 shift ctrl "t" 新标签 alt 1,…