当前位置: 首页 > news >正文

ASP.NET Core 实战:JWT 身份验证

一、引言

在当今数字化时代,Web 应用的安全性至关重要。ASP.NET Core 作为一种广泛应用的开发框架,为开发者提供了强大的工具来构建安全可靠的应用程序。而 JWT(JSON Web Token)身份验证则是保障应用安全的关键环节之一。

JWT 身份验证在ASP.NET Core 开发中具有举足轻重的地位。它不仅能够有效地验证用户身份,确保只有合法用户能够访问受保护的资源,还能为应用提供跨域支持、无状态性等优势,极大地提升了应用的安全性和性能。

本文将深入探讨 JWT 身份验证在ASP.NET Core 中的原理、实现步骤以及应用场景。通过详细的讲解和丰富的代码示例,帮助开发者全面掌握这一技术,为构建安全可靠的 Web 应用奠定坚实的基础。

二、JWT 基础入门

2.1 什么是 JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。它是一种紧凑、自包含的令牌,常用于在网络应用中进行身份验证和授权。在 Web 开发中,JWT 作为用户身份验证和授权的重要工具,被广泛应用于各种场景。例如,在前后端分离的架构中,前端应用通过 JWT 与后端 API 进行通信,确保只有经过认证的用户能够访问受保护的资源。在分布式系统中,JWT 可以在不同的服务之间传递用户身份信息,实现统一的身份验证和授权机制。

2.2 JWT 的结构剖析

JWT 由三个部分组成:头部(Header)、负载(Payload)和签名(Signature)。

头部通常包含两部分信息:令牌的类型(如 JWT)和使用的签名算法(如 HMAC SHA256 或 RSA)。一个示例头部如下:

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

然后将其进行 Base64Url 编码,形成 JWT 的第一部分。

负载是 JWT 的主体部分,包含了实际需要传输的数据,也被称为声明(Claims)。声明可以是用户的身份信息、权限、过期时间等。例如:

{"sub": "1234567890","name": "John Doe","iat": 1516239022,"exp": 1516242622
}

这里的 “sub” 代表主题,“name” 是用户名,“iat” 是签发时间,“exp” 是过期时间。同样,将负载进行 Base64Url 编码,得到 JWT 的第二部分。

签名部分用于验证数据的完整性和签发者的身份。它的生成需要使用编码后的头部、编码后的负载、一个密钥(secret)以及头部中指定的签名算法。例如,如果使用 HMAC SHA256 算法,签名的生成方式如下:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

将上述三个部分用点号(.)连接起来,就构成了一个完整的 JWT。例如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoiMTUxNjIzOTAyMzIiLCJleHAiOjE1MTYyNzI2MjB9.M5KXaR7XQWjg3Ri1cJtO-Q6s6cZgP85zS2j-lPc5WUc。

2.3 JWT 工作原理

在身份验证流程中,JWT 的工作方式如下:

  1. 生成:用户在客户端输入用户名和密码进行登录,服务器验证用户的凭据。如果验证通过,服务器会根据用户信息生成一个 JWT。服务器会将用户的相关信息(如用户 ID、用户名、角色等)放入 JWT 的负载部分,并使用指定的签名算法和密钥对头部和负载进行签名,生成完整的 JWT。

  2. 传输:服务器将生成的 JWT 返回给客户端。客户端通常会将 JWT 存储在本地,如浏览器的本地存储或 Cookie 中。在后续的每一次请求中,客户端会将 JWT 包含在请求头中发送给服务器。例如,在 HTTP 请求中,可以将 JWT 放在Authorization头字段中,格式为Bearer 。

  3. 验证:服务器接收到请求后,会从请求头中提取 JWT,并使用相同的密钥和签名算法对 JWT 进行验证。服务器会验证签名的有效性,以确保 JWT 没有被篡改。同时,服务器还会检查 JWT 的过期时间、签发者等信息是否符合预期。如果验证通过,服务器就可以从 JWT 的负载中获取用户信息,并根据这些信息进行授权决策,决定是否允许用户访问请求的资源。

三、JWT 在ASP.NET Core 中的优势

3.1 无状态性与服务器减负

在传统的会话管理模式中,服务器需要在会话中存储大量用户相关信息,这不仅占用服务器宝贵的内存资源,还会增加服务器的管理复杂度。随着用户数量的增加和并发请求的增多,服务器的负担会越来越重,可能导致性能下降甚至系统崩溃。

与之形成鲜明对比的是,JWT 具有出色的无状态性。每个 JWT 都包含了足够的用户身份和权限信息,服务器在接收到请求时,无需再去查询数据库或读取会话信息来验证用户身份和权限。这使得服务器能够专注于处理业务逻辑,极大地减少了服务器的存储负担和处理开销。在高并发场景下,服务器可以轻松应对大量请求,无需担心会话管理带来的性能瓶颈,从而提高了系统的可扩展性和稳定性。

3.2 安全性保障

JWT 通过签名机制为数据的完整性和真实性提供了坚实的保障。签名的生成需要使用特定的密钥和签名算法,只有拥有正确密钥的服务器才能对 JWT 进行签名和验证。当客户端将 JWT 发送给服务器时,服务器会使用相同的密钥和算法对签名进行验证。如果 JWT 在传输过程中被恶意篡改,签名验证将失败,服务器将拒绝该请求,从而有效地防止了数据被篡改和伪造的风险。

此外,JWT 还支持使用 HTTPS 协议进行传输,进一步增强了数据的安全性。HTTPS 协议通过加密通信内容,防止数据在传输过程中被窃取或监听,确保用户信息的安全。

3.3 跨平台与跨语言支持

JWT 基于标准的 JSON 格式,这使得它在不同平台和编程语言之间能够实现无缝集成。无论是在前端的 JavaScript 应用、后端的ASP.NET Core 服务,还是在移动应用开发中,都可以轻松地使用和处理 JWT。在前后端分离的架构中,前端可以使用 JavaScript 库来生成和验证 JWT,而后端的ASP.NET Core 应用则可以通过相应的中间件来处理 JWT 认证。在移动应用中,也可以使用 JWT 与后端服务器进行安全通信。

这种跨平台和跨语言的特性,使得 JWT 成为了构建分布式系统和多平台应用的理想选择,能够满足不同开发团队和项目的需求。

四、ASP.NET Core 实现 JWT 身份验证

4.1 创建ASP.NET Core 项目

在开始实现 JWT 身份验证之前,我们首先需要创建一个新的ASP.NET Core 项目。这可以通过命令行工具或 Visual Studio 来完成。

使用命令行工具时,确保你已经安装了.NET SDK。打开命令提示符或终端,运行以下命令:

dotnet new webapi -o MyJwtApp
cd MyJwtApp

这将创建一个名为MyJwtApp的新ASP.NET Core Web API 项目,并进入该项目目录。

如果你更喜欢使用 Visual Studio,打开 Visual Studio 后,选择 “创建新项目”。在项目模板中,选择 “ASP.NET Core Web 应用程序”,然后点击 “下一步”。为项目命名并选择保存位置,点击 “创建”。在创建项目的对话框中,选择 “API” 模板,然后点击 “创建”。这样就创建了一个新的ASP.NET Core Web API 项目。

4.2 安装必要的 NuGet 包

要在ASP.NET Core 项目中实现 JWT 身份验证,我们需要安装一些必要的 NuGet 包。主要包括Microsoft.AspNetCore.Authentication.JwtBearer和System.IdentityModel.Tokens.Jwt。

Microsoft.AspNetCore.Authentication.JwtBearer包提供了 JWT 承载令牌的身份验证处理程序,用于验证 JWT 令牌。System.IdentityModel.Tokens.Jwt包则提供了用于处理 JWT 令牌的类和方法,包括创建、验证和解析 JWT 令牌。

使用命令行工具安装这些包,可以在项目目录中运行以下命令:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt

如果你使用的是 Visual Studio,可以通过 “管理 NuGet 程序包” 来安装这些包。在解决方案资源管理器中,右键点击项目,选择 “管理 NuGet 程序包”。在 NuGet 包管理器中,搜索并安装Microsoft.AspNetCore.Authentication.JwtBearer和System.IdentityModel.Tokens.Jwt包。

4.3 配置 JWT 身份验证

安装好所需的 NuGet 包后,我们需要在Startup.cs文件中配置 JWT 身份验证。

在Startup.cs的ConfigureServices方法中,添加以下代码:

using Microsoft.IdentityModel.Tokens;
using System.Text;public void ConfigureServices(IServiceCollection services)
{services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = Configuration["Jwt:Issuer"],ValidAudience = Configuration["Jwt:Audience"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))};});
}

在这段代码中,我们首先调用AddAuthentication方法,启用 JWT 身份验证,并指定使用 JWT 承载令牌的身份验证方案。然后,通过AddJwtBearer方法配置 JWT 身份验证的选项。TokenValidationParameters用于定义令牌验证的参数,包括验证发行者、受众、有效期以及签名密钥。这里的ValidIssuer、ValidAudience和IssuerSigningKey分别从配置文件中读取,确保了配置的灵活性和安全性。

同时,我们需要在appsettings.json文件中添加 JWT 的配置信息,如下所示:

{"Jwt": {"Issuer": "yourIssuer","Audience": "yourAudience","Key": "yourSecretKey"}
}

请将yourIssuer、yourAudience和yourSecretKey替换为你自己的颁发者、受众和密钥。密钥应该是一个足够长且安全的字符串,用于签名和验证 JWT 令牌。

在Startup.cs的Configure方法中,添加以下代码来启用身份验证和授权中间件:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
}

这段代码确保了应用程序在处理请求时,会先进行身份验证,然后进行授权,最后将请求映射到相应的控制器。

4.4 创建用户实体类与登录逻辑

接下来,我们需要定义一个用户实体类来表示用户信息,并编写一个控制器来处理用户的登录请求,生成 JWT 令牌。

创建一个名为User.cs的类文件,定义用户实体类:

public class User
{public string Username { get; set; }public string Password { get; set; }
}

这个简单的用户实体类包含了用户名和密码两个属性,用于表示用户的登录信息。

然后,创建一个名为AccountController.cs的控制器文件,编写登录逻辑:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{private readonly IConfiguration _configuration;public AccountController(IConfiguration configuration){_configuration = configuration;}[HttpPost("login")]public IActionResult Login([FromBody] User user){// 这里只是一个简单的示例,实际应用中应该验证用户名和密码if (user.Username == "admin" && user.Password == "password"){var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, user.Username),new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(issuer: _configuration["Jwt:Issuer"],audience: _configuration["Jwt:Audience"],claims: claims,expires: DateTime.Now.AddMinutes(30),signingCredentials: credentials);return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token) });}return Unauthorized();}
}

在这个控制器中,Login方法处理用户的登录请求。首先,它从请求体中获取用户输入的用户名和密码(实际应用中应该从数据库或其他存储中验证用户名和密码的正确性)。如果用户名和密码匹配,它会创建一个包含用户信息的声明集合(claims),然后使用之前配置的密钥和签名算法创建一个 JWT 令牌。最后,将生成的 JWT 令牌返回给客户端。如果用户名和密码不匹配,则返回Unauthorized状态码,表示未经授权。

4.5 保护控制器

为了确保只有持有有效 JWT 令牌的用户才能访问特定的控制器和方法,我们可以使用[Authorize]特性。

创建一个名为ProtectedController.cs的控制器文件,如下所示:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;[Authorize]
[ApiController]
[Route("protected")]
public class ProtectedController : ControllerBase
{[HttpGet("data")]public IActionResult GetData(){return Ok(new { message = "This is protected data." });}
}

在这个控制器类上,我们使用了[Authorize]特性,表示该控制器下的所有方法都需要进行身份验证。只有当客户端在请求头中包含有效的 JWT 令牌时,才能访问GetData方法,获取受保护的数据。如果没有提供有效的 JWT 令牌,或者令牌无效,请求将被拒绝,并返回401 Unauthorized状态码。通过这种方式,我们可以轻松地保护需要授权才能访问的资源,确保应用程序的安全性。

五、代码实战与案例分析

5.1 完整代码示例展示

下面是一个完整的ASP.NET Core 项目示例,展示了如何实现 JWT 身份验证。

项目结构

MyJwtApp
│
├── Controllers
│   ├── AccountController.cs
│   └── ProtectedController.cs
│
├── Models
│   └── User.cs
│
├── appsettings.json
│
├── Program.cs
│
└── Startup.cs

User.cs**(用户实体类)**:

namespace MyJwtApp.Models;public class User
{public string Username { get; set; }public string Password { get; set; }
}

AccountController.cs**(处理登录逻辑)**:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using MyJwtApp.Models;namespace MyJwtApp.Controllers;[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{private readonly IConfiguration _configuration;public AccountController(IConfiguration configuration){_configuration = configuration;}[HttpPost("login")]public IActionResult Login([FromBody] User user){// 这里只是一个简单的示例,实际应用中应该验证用户名和密码if (user.Username == "admin" && user.Password == "password"){var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, user.Username),new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(issuer: _configuration["Jwt:Issuer"],audience: _configuration["Jwt:Audience"],claims: claims,expires: DateTime.Now.AddMinutes(30),signingCredentials: credentials);return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token) });}return Unauthorized();}
}

ProtectedController.cs**(受保护的控制器)**:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;namespace MyJwtApp.Controllers;[Authorize]
[ApiController]
[Route("protected")]
public class ProtectedController : ControllerBase
{[HttpGet("data")]public IActionResult GetData(){return Ok(new { message = "This is protected data." });}
}

Startup.cs**(配置 JWT 身份验证)**:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using System.Text;namespace MyJwtApp;public class Startup
{public IConfiguration Configuration { get; }public Startup(IConfiguration configuration){Configuration = configuration;}public void ConfigureServices(IServiceCollection services){services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = Configuration["Jwt:Issuer"],ValidAudience = Configuration["Jwt:Audience"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))};});services.AddControllers();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}
}

appsettings.json**(配置文件)**:

{"Jwt": {"Issuer": "yourIssuer","Audience": "yourAudience","Key": "yourSecretKey"},"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*"
}

在这个示例中,我们创建了一个简单的ASP.NET Core Web API 项目,包含了用户登录、生成 JWT 令牌以及保护受限制资源的功能。通过配置Startup.cs文件和编写相应的控制器,实现了 JWT 身份验证的基本流程。

5.2 案例场景模拟

假设我们正在开发一个用户管理系统,该系统包含用户注册、登录、查看个人信息以及管理用户角色等功能。在这个系统中,JWT 身份验证可以如下使用:

  1. 用户注册:用户在前端页面填写注册信息,发送到后端服务器。后端服务器将用户信息存储到数据库中。

  2. 用户登录:用户在登录页面输入用户名和密码,发送到后端的AccountController的Login方法。如果用户名和密码验证通过,服务器会生成一个 JWT 令牌,并返回给客户端。客户端将 JWT 令牌存储在本地,例如浏览器的本地存储或 Cookie 中。

  3. 访问受保护资源:当用户想要查看个人信息或执行其他需要授权的操作时,客户端会在请求头中包含 JWT 令牌,发送到后端服务器。例如,当用户访问ProtectedController的GetData方法时,服务器会验证请求头中的 JWT 令牌。如果令牌有效,服务器会返回相应的受保护数据;如果令牌无效或缺失,服务器会返回401 Unauthorized状态码。

  4. 用户角色管理:在实际应用中,JWT 的负载部分可以包含用户的角色信息。例如,管理员用户的 JWT 令牌中可能包含 “Admin” 角色声明。当用户尝试访问特定的管理功能时,服务器可以根据 JWT 中的角色信息进行授权判断。只有具有 “Admin” 角色的用户才能访问管理相关的控制器和方法。

通过这样的方式,JWT 身份验证在用户管理系统中有效地保障了用户数据的安全性和系统的访问控制。

5.3 代码解析与优化建议

  1. 关键代码解析
    • AccountController中的Login****方法:该方法负责处理用户的登录请求。它首先从请求体中获取用户输入的用户名和密码,然后进行简单的验证(实际应用中应从数据库验证)。如果验证通过,它会创建一个包含用户信息的声明集合(claims),这些声明将被包含在 JWT 令牌中。接着,使用配置的密钥和签名算法创建一个JwtSecurityToken对象,并通过JwtSecurityTokenHandler将其转换为字符串形式的 JWT 令牌,最后返回给客户端。
    • Startup.cs中的ConfigureServices****方法:在这个方法中,我们配置了 JWT 身份验证。通过AddAuthentication方法启用 JWT 身份验证,并指定使用 JWT 承载令牌的身份验证方案。AddJwtBearer方法用于配置 JWT 身份验证的具体选项,其中TokenValidationParameters定义了令牌验证的规则,包括验证发行者、受众、有效期以及签名密钥。这些参数确保了只有合法的 JWT 令牌才能通过验证。
  1. 优化建议
    • 密钥管理:在实际应用中,不要将 JWT 的签名密钥硬编码在代码中,如示例中的yourSecretKey。这是非常不安全的做法,一旦代码泄露,恶意用户就可以伪造 JWT 令牌。建议将密钥存储在环境变量、配置文件或使用专门的密钥管理服务(如 Azure Key Vault 或 HashiCorp Vault)中。在Startup.cs中读取密钥时,可以从环境变量或配置文件中获取,例如:
var key = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWT_SECRET_KEY"));
  • 令牌过期时间设置:合理设置 JWT 令牌的过期时间非常重要。如果过期时间设置过长,一旦令牌泄露,恶意用户将有较长时间可以使用该令牌访问受保护资源;如果过期时间设置过短,用户可能需要频繁重新登录,影响用户体验。应根据应用的安全需求和用户使用场景来确定合适的过期时间。例如,对于一些对安全性要求较高的操作,可以设置较短的过期时间,如几分钟;对于一般的用户会话,可以设置为几小时或一天。在AccountController的Login方法中创建 JWT 令牌时,可以根据需求调整expires参数:
var token = new JwtSecurityToken(issuer: _configuration["Jwt:Issuer"],audience: _configuration["Jwt:Audience"],claims: claims,expires: DateTime.Now.AddHours(1), // 设置为1小时过期signingCredentials: credentials);
  • 刷新令牌机制:考虑实现刷新令牌机制,以解决 JWT 令牌过期后用户需要重新登录的问题。刷新令牌是一个长期有效的令牌,用于在 JWT 令牌过期时获取新的 JWT 令牌。当 JWT 令牌过期后,客户端可以使用刷新令牌向服务器请求新的 JWT 令牌,而无需用户重新输入用户名和密码。这样可以提高用户体验,同时保持系统的安全性。实现刷新令牌机制需要在数据库中存储用户的刷新令牌,并在服务器端进行相应的验证和处理逻辑。

  • 错误处理:在当前的代码中,对于身份验证失败的情况,只是简单地返回了Unauthorized状态码。在实际应用中,应提供更详细的错误信息,以便客户端能够更好地处理身份验证失败的情况。例如,可以返回一个包含错误消息的 JSON 响应,告诉用户是用户名或密码错误,还是令牌无效等具体原因。在AccountController的Login方法中,可以如下修改:

if (user.Username!= "admin" || user.Password!= "password")
{return Unauthorized(new { error = "用户名或密码错误" });
}
  • 数据加密:确保所有敏感信息,如用户密码,在存储和传输过程中都经过加密处理。在用户注册时,应使用强加密算法(如 BCrypt 或 Argon2)对用户密码进行加密存储,而不是明文存储。在数据传输过程中,使用 HTTPS 协议来加密数据,防止数据被窃取或篡改。可以在Startup.cs的Configure方法中添加如下代码来强制使用 HTTPS:
app.UseHttpsRedirection();

通过以上优化建议,可以进一步提高 JWT 身份验证在ASP.NET Core 应用中的安全性和用户体验。

六、常见问题与解决方案

6.1 令牌过期处理

令牌过期是 JWT 身份验证中常见的问题之一。当令牌过期后,用户将无法访问受保护的资源。为了解决这个问题,常见的解决方案是采用刷新令牌机制。

刷新令牌是一个长期有效的令牌,用于在访问令牌过期时获取新的访问令牌。在用户登录时,服务器不仅返回访问令牌,还会返回一个刷新令牌。当访问令牌过期后,客户端可以使用刷新令牌向服务器发送请求,获取新的访问令牌。服务器在接收到刷新令牌后,会验证其有效性。如果刷新令牌有效,服务器会生成一个新的访问令牌,并返回给客户端。

在ASP.NET Core 中实现刷新令牌机制,可以通过创建一个新的控制器方法来处理刷新令牌的请求。例如:

[HttpPost("refresh")]
public IActionResult Refresh([FromBody] TokenRefreshRequest request)
{var principal = GetPrincipalFromExpiredToken(request.AccessToken);var username = principal.Identity.Name;var user = _userService.GetUserByUsername(username);if (user == null || user.RefreshToken!= request.RefreshToken){return Unauthorized();}var newAccessToken = GenerateAccessToken(principal.Claims);var newRefreshToken = GenerateRefreshToken();user.RefreshToken = newRefreshToken;_userService.UpdateUser(user);return Ok(new { AccessToken = newAccessToken, RefreshToken = newRefreshToken });
}

在这个示例中,TokenRefreshRequest是一个包含过期访问令牌和刷新令牌的模型。GetPrincipalFromExpiredToken方法用于从过期的访问令牌中提取用户信息。服务器会验证刷新令牌的有效性,并检查用户是否存在以及刷新令牌是否匹配。如果验证通过,服务器会生成新的访问令牌和刷新令牌,并更新用户的刷新令牌。

通过这种方式,用户在访问令牌过期后,无需重新登录,即可通过刷新令牌获取新的访问令牌,继续访问受保护的资源,提高了用户体验。

6.2 安全漏洞防范

在使用 JWT 身份验证时,需要注意防范一些潜在的安全漏洞。

中间人攻击:中间人攻击是指攻击者在通信过程中拦截并篡改数据。为了防止中间人攻击,应确保在传输 JWT 时使用 HTTPS 协议。HTTPS 通过加密通信内容,使得攻击者无法窃取或篡改 JWT 令牌。在ASP.NET Core 中,可以通过配置 Kestrel 服务器或使用反向代理(如 IIS 或 Nginx)来启用 HTTPS。在Startup.cs的Configure方法中,可以添加如下代码来强制使用 HTTPS:

app.UseHttpsRedirection();

令牌泄露:如果 JWT 令牌泄露,恶意用户可能会使用该令牌访问受保护的资源。为了防止令牌泄露,应采取以下措施:

  • 安全存储:在客户端,应将 JWT 存储在安全的位置,如 HTTP-only Cookie 或本地存储中,并确保页面没有 XSS 漏洞,防止令牌被窃取。在服务器端,签名密钥应妥善保管,不要将密钥硬编码在代码中,建议使用环境变量或配置文件来存储密钥。

  • 设置合理的过期时间:合理设置 JWT 的过期时间,避免令牌过期时间过长。较短的过期时间可以减少令牌泄露后的风险。同时,可以结合刷新令牌机制,在令牌过期时及时获取新的令牌。

  • 监控和审计:对 JWT 的使用进行监控和审计,及时发现异常的令牌使用情况。例如,可以记录每个 JWT 的使用时间、IP 地址等信息,以便在出现问题时进行追溯和分析。

6.3 配置错误排查

在配置 JWT 身份验证时,可能会出现一些错误,导致身份验证无法正常工作。以下是一些常见配置错误的排查方法和解决方案:

  • 密钥配置错误:如果签名密钥配置错误,服务器将无法验证 JWT 令牌的有效性。确保在appsettings.json文件中正确配置了 JWT 的密钥,并且在Startup.cs中读取密钥的方式正确。检查密钥的长度和格式是否符合要求,避免使用过于简单的密钥。

  • 令牌验证参数错误:在Startup.cs中配置TokenValidationParameters时,确保各个参数的设置正确。例如,ValidateIssuer、ValidateAudience、ValidateLifetime等参数应根据实际需求进行设置。如果这些参数设置错误,可能会导致令牌验证失败。

  • 包引用问题:确保项目中正确引用了所需的 NuGet 包,并且包的版本兼容。如果包引用不正确或版本不兼容,可能会导致编译错误或运行时异常。可以通过检查项目的依赖项和 NuGet 包管理器来确认包的引用情况。

  • 控制器和方法的授权特性错误:如果在控制器或方法上使用了[Authorize]特性,但身份验证仍然无法正常工作,检查是否正确配置了身份验证中间件,并且[Authorize]特性的使用是否正确。确保需要授权的控制器和方法都正确标记了[Authorize]特性,并且该特性没有被其他配置覆盖。

通过仔细排查这些常见的配置错误,可以有效地解决 JWT 身份验证中出现的问题,确保应用程序的安全性和稳定性。

七、总结与展望

在ASP.NET Core 中实现 JWT 身份验证,为构建安全可靠的 Web 应用提供了有力保障。通过本文的详细介绍,我们深入了解了 JWT 的基本概念、结构和工作原理,掌握了在ASP.NET Core 项目中实现 JWT 身份验证的具体步骤,包括创建项目、安装 NuGet 包、配置身份验证、编写登录逻辑以及保护控制器等。

在实际应用中,JWT 身份验证展现出诸多优势,如无状态性减轻服务器负担、强大的安全性保护数据、跨平台支持适应多样化开发需求等。通过合理的代码实现和优化,能够有效提升应用的安全性和用户体验。

展望未来,随着 Web 技术的不断发展,JWT 身份验证有望在更多领域得到广泛应用。例如,在微服务架构中,JWT 可以作为不同服务之间身份验证和授权的统一标准,实现更加灵活和高效的服务间通信。同时,随着对数据安全要求的不断提高,JWT 的安全性也将不断提升,新的签名算法和加密技术可能会被应用到 JWT 中,进一步增强其抵御安全威胁的能力。

相信在未来的 Web 开发中,JWT 身份验证将继续发挥重要作用,为开发者提供更加安全、可靠的身份验证解决方案。

相关文章:

ASP.NET Core 实战:JWT 身份验证

一、引言 在当今数字化时代,Web 应用的安全性至关重要。ASP.NET Core 作为一种广泛应用的开发框架,为开发者提供了强大的工具来构建安全可靠的应用程序。而 JWT(JSON Web Token)身份验证则是保障应用安全的关键环节之一。 JWT 身…...

【学习笔记15】如何在非root服务器中,安装属于自己的redis

一、下载安装包 官网下载黑马程序员给的安装包(redis-6.2.6) 二、将安装包上传至服务器 我将安装包上传在我的文件夹/home/XXX,指定路径中/src/local/redis/,绝对路径为/home/XXX/src/local/redis/解压安装包 XXXomega:~$ cd …...

基于深度学习的微出血自动检测及解剖尺度定位|文献速递-视觉大模型医疗图像应用

Title 题目 Toward automated detection of microbleeds with anatomical scale localization using deep learning 基于深度学习的微出血自动检测及解剖尺度定位 01 文献速递介绍 基于深度学习的脑微出血(CMBs)检测与解剖定位 脑微出血&#xff…...

Couchbase UI: Dashboard

以下是 Couchbase UI Dashboard 页面详细介绍,包括页面布局和功能说明,帮助你更好地理解和使用。 1. 首页(Overview) 功能:提供集群的整体健康状态和性能摘要 集群状态 节点健康状况:绿色(正…...

Python

1 变量 1.1 定义 变量:为快速定义目标,将数据在内存占据的存储空间分配的一个名称。 定义:变量名 数据值 作用:临时存储数据 message "hello" print(message)#输出:hello message "hello Pytho…...

一个软件分发和下载的网站源码,带多套模板

PHP游戏应用市场APP软件下载平台网站源码手机版 可自行打包APP,带下载统计,带多套模板,带图文教程 代码下载:百度网盘...

war包 | Docker部署flowable-ui

文章目录 引言I war包部署flowable-ui下载war包配置Tomcat访问 flowable-uiII Docker启动flowable-ui并修改配置Docker启动flowable-ui修改配置访问Flowable UI界面。III 知识扩展加速源docker run -i -t -d 参数引言 Flowable 支持 BPMN 2.0 行业标准,同时提供了一些 Flowab…...

07_游戏加载窗口

隐藏动态提示窗口 创建空节点 命名为 LoadingWnd 意为加载窗口 并设置全屏 在子级下创建Image作为加载背景 也设置成全屏 将以下资源放进Art文件夹中 设置好精灵模式后拖拽至 Image的Source Image框选 创建文本作为提示内容 增加描边组件OutLine可以美化字体 创建Image作为加载…...

proxyman抓包Java中feign请求以及断点请求响应内容修改或流转到本地

proxyman抓包Java中feign请求以及断点请求响应内容修改或流转到本地 配置流程第一步: 借助arthas配置请求代理第二步: 借助proxyman配置远程映射第三步: 借助SwitchHosts配置hosts域名最后: 借助ssh的LocalForward功能, 打通网络(这步网络不通才需要) 最近在修bug的过程中, 因为…...

PyTorch使用教程(10)-torchinfo.summary网络结构可视化详细说明

1、基本介绍 torchinfo是一个为PyTorch用户量身定做的开源工具,其核心功能之一是summary函数。这个函数旨在简化模型的开发与调试流程,让模型架构一目了然。通过torchinfo的summary函数,用户可以快速获取模型的详细结构和统计信息&#xff0…...

centos9编译安装opensips 二【进阶篇-定制目录+模块】推荐

环境:centos9 last opensips -V version: opensips 3.6.0-dev (x86_64/linux) flags: STATS: On, DISABLE_NAGLE, USE_MCAST, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, HP_MALLOC, DBG_MALLOC, CC_O0, FAST_LOCK-ADAPTIVE_WAIT ADAPTIVE_WAIT_LOOPS1024, MAX_RE…...

MongoDB 备份与恢复综述

目录 一、基本概述 二、逻辑备份 1、全量备份 2、增量备份 3、恢复 三、物理备份 1、cp/tar/fsync 2、WiredTiger 热备份 3、恢复 四、快照备份 一、基本概述 MongoDB 是一种流行的 NoSQL 数据库,它使用文档存储数据,支持丰富的查询语言和索引…...

Apache Hive3定位表并更改其位置

Apache Hive3表 1、Apache Hive3表概述2、Hive3表存储格式3、Hive3事务表4、Hive3外部表5、定位Hive3表并更改位置6、使用点表示法引用表7、理解CREATE TABLE行为 1、Apache Hive3表概述 Apache Hive3表类型的定义和表类型与ACID属性的关系图使得Hive表变得清晰。表的位置取决于…...

Flutter项目和鸿蒙平台的通信

Flutter项目和鸿蒙平台的通信 前言Flutter和Harmonyos通信MethodChannelBasicMessageChannelEventChannel 前言 大家在使用Flutter开发项目的时候, Flutter提供了Platfrom Channel API来和个个平台进行交互。 Flutter官方目前提供了一下三种方式来和个个平台交互&…...

5. 马科维茨资产组合模型+政策意图AI金融智能体(Qwen-Max)增强方案(理论+Python实战)

目录 0. 承前1. AI金融智能体1.1 What is AI金融智能体1.2 Why is AI金融智能体1.3 How to AI金融智能体 2. 数据要素&计算流程2.1 参数集设置2.2 数据获取&预处理2.3 收益率计算2.4 因子构建与预期收益率计算2.5 协方差矩阵计算2.6 投资组合优化2.7 持仓筛选2.8 AI金融…...

嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础

嵌入式知识点总结 ARM体系与架构 专题提升(一)-硬件基础 目录 1.NAND FLASH 和NOR FLASH异同 ? 2.CPU,MPU,MCU,SOC,SOPC联系与差别? 3.什么是交叉编译? 4.为什么要交叉编译? 5.描述一下嵌入式基于ROM的运行方式和基于RAM的运行方式有什么区别? 1…...

thinkphp8在使用apidoc时, 4层的接口会有问题 解决办法

thinkphp8 4层的接口会有问题, 比如这样的接口 /adminapi/notice/announcements/lists, 应该换成 /adminapi/notice.announcements/lists 这样才行, 有没有人处理过? 实际上在官网的帮助里有描述 自动生成的url不对? | Apidoc // config/apidoc.php //... auto_url…...

【jmeter】下载及使用教程【mac】

1.安装java 打开 Java 官方下载网站https://www.oracle.com/java/technologies/downloads/选择您想要下载的 Java 版本,下载以 .dmg 结尾的安装包,注意 JMeter 需要 Java 8下载后打开安装包点击“安装”按钮即可 2.下载jmeter 打开 Apache JMeter 官方…...

C# ASP.NET MVC项目内使用ApiController

1.在App_Start文件夹新建WebApiConfig.cs文件,建立webApi路由的注册方法。 using System.Web.Http;namespace PrivilegeManager {public class WebApiConfig{public static void Register(HttpConfiguration config){config.MapHttpAttributeRoutes();config.Route…...

Langchain+FastApi+Vue前后端Ai对话(超详细)

一、引入 首先可以先看下作者的文章 FastApi相关文章:创建最简单FastApi的项目Vue相关文章:最简单的aixos二次封装Langchain相关文章:如何使用LangSmith跟踪deepseek模型 二、后端搭建 1 项目文件结构 routers:存放api接口se…...

【电脑无法通过鼠标和键盘唤醒应该怎么办】

【电脑无法通过鼠标和键盘唤醒应该怎么办】 方法一(有时候不起作用):方法二(方法一无效时,使用方法二): 方法一(有时候不起作用): 方法二(方法一无效时,使用方法二):...

OpenCV相机标定与3D重建(65)对图像点进行去畸变处理函数undistortPoints()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 从观测到的点坐标计算理想点坐标。 该函数类似于 undistort 和 initUndistortRectifyMap,但它操作的是稀疏点集而不是光栅图像。此外…...

Logo语言的函数实现

Logo语言的函数实现 引言 Logo语言是一种教育性编程语言,最初由西摩尔派普特(Seymour Papert)在1960年代开发。它以“海龟图形”(Turtle Graphics)而闻名,通过简单的命令控制一只“海龟”在屏幕上绘制图形…...

前沿技术对比:大模型技术为什么发展远快于区块链技术,中英对照解释

文章目录 前言1、技术复杂性与成熟度 / Technical Complexity and Maturity2.、应用场景与行业需求 / Application Scenarios and Industry Demand3、监管与法律问题 / Regulatory and Legal Issues4、去中心化与网络效应 / Decentralization and Network Effects5、能源消耗与…...

Java设计模式 九 桥接模式 (Bridge Pattern)

桥接模式 (Bridge Pattern) 桥接模式是一种结构型设计模式,它的核心思想是将抽象部分与实现部分分离,使它们可以独立变化。这种模式通过组合而不是继承的方式来扩展功能,从而减少类之间的耦合度。 1. 模式结构 桥接模式的结构包括以下角色&…...

stm8s单片机(二)外部中断实验

中断优先级 stm8的中断优先级不是固定不变的,stm8的中断分为硬件优先级与软件优先级;当多个中断发生时,cpu会先响应软件优先级高的中断,若软件优先级相同会先响应硬件优先级高的; 其中软件优先级有四个 /*** brief …...

计算机网络 (53)互联网使用的安全协议

一、SSL/TLS协议 概述: SSL(Secure Sockets Layer)安全套接层和TLS(Transport Layer Security)传输层安全协议是工作在OSI模型应用层的安全协议。SSL由Netscape于1994年开发,广泛应用于基于万维网的各种网络…...

数学基础 --线性代数之理解矩阵乘法

理解矩阵乘法的解析 矩阵乘法(Matrix Multiplication)是线性代数中的核心操作之一。在数学、几何和工程实际中,它不仅是一种代数运算规则,还承载着丰富的几何和映射意义。本文将从多个角度深入解析矩阵乘法,帮助读者理…...

数学规划问题2 .有代码(非线性规划模型,最大最小化模型,多目标规划模型)

非线性规划模型 FIrst:转化为标准型 在matlab中求非线性规划的函数 练习题: 典型例题: 最大最小化模型 核心思想: matlab的模型求解 经典例题: 多目标规划模型 基本概念 求解思路: 模型构建步骤 经典例题: 非线性规划模型 非线性规划(Nonl…...

jax 和 jaxlib 的 cuda 版本安装

笔者花费时间才在 Ubuntu 20.04 适配上 jax 和 jaxlib 的 cuda 版本安装,以及 chex 版本。 版本展示 本人版本展示 jax0.4.27 ,jaxlib0.4.27cuda12.cudnn89,chex0.1.86。 安装过程 cuda 以及环境变量配置过程 首先安装cuda12.4和cudnn8.9&…...

Spring Boot MyBatis Plus 版本兼容问题(记录)

Spring Boot & MyBatis Plus 版本兼容问题(Invalid value type for attribute factoryBeanObjectType: java.lang.String) 问题描述问题排查1. 检查 MapperScan 的路径2. 项目中没有配置 FactoryBean3. 检查 Spring 和 MyBatis Plus 版本兼容性 解决…...

Ubuntu如何安装redis服务?

环境: Ubuntu22.04 WSL2 问题描述: 如何安装redis服务? 解决方案: 1.在 Linux 上(如 Ubuntu/Debian)安装 1.通过包管理工具安装 Redis 服务器: sudo apt update sudo apt install redis…...

FFmpeg 头文件完美翻译之 libavcodec 模块

前言 众所周知,FFmpeg 的代码开发上手难度较高,源于官方提供的文档很少有包含代码教程相关的。要想熟练掌握 FFmpeg 的代码库开发,需要借助它的头文件,FFmpeg 把很多代码库教程都写在头文件里面。因此,熟读头文件的内…...

设计模式的艺术-单一职责原则

1.基础知识 是最简单的面向对象设计原则,它用于控制类的粒度大小。 一个类只负责一个功能领域中的相应职责。 单一职责原则的核心思想是:一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法&…...

MySQL主从配置

一、 主从原理 MySQL 主从同步是一种数据库复制技术,它通过将主服务器上的数据更改复制到一个或多个从服务器,实现数据的自动同步。主从同步的核心原理是将主服务器上的二进制日志复制到从服务器,并在从服务器上执行这些日志中的操作。 二、主…...

【Unity3D实现雨下在窗户上的效果】

系列文章目录 unity工具 文章目录 系列文章目录👉前言👉一、效果展示👉二、原理👉三、使用步骤3-1、shader代码纹理映射数学运算和函数的运用特效算法的实现高效的性能优化👉壁纸分享👉总结👉前言 想要好看的效果肯定是要用shader实现啦,为什么呢? 因为Shade…...

k8s资源预留

k8s资源预留 https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/reserve-compute-resources/ vim /var/lib/kubelet/config.yamlenforceNodeAllocatable: - pods kubeReserved: # 配置 kube 资源预留cpu: 500mmemory: 1Giephemeral-storage: 1Gi systemReserved: #…...

云计算与物联网技术的融合应用(在工业、农业、家居、医疗、环境、城市等整理较全)

摘要 为生产领域带来更加全面和深入的变革。通过云计算平台对物联网数据进行处理和分析,企业可以实现对生产过程的更加精细化的管理和控制。 1. 智能生产调度 通过云计算和物联网技术的融合应用,企业可以实现对生产线上各个环节的实时监控和数据分析。…...

【深度学习】2.视觉问题与得分函数

计算机视觉任务 可以通过神经网络搜索是什么类别的动物。 图像实际就是含有数值的三维矩阵。 像素值从0-255可以表示亮度递增的参数。数字越大,像素点越亮。 最后的3表示三个颜色通道,常见的如JPG、RGB等。 现实场景容易发生各种遮蔽现象。 计算机判断…...

Node.js——express中间件(全局中间件、路由中间件、静态资源中间件)

个人简介 👀个人主页: 前端杂货铺 🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…...

安卓动态设置Unity图形API

命令行方式 Unity图像api设置为自动,安卓动态设置Vulkan、OpenGLES Unity设置 安卓设置 创建自定义活动并将其设置为应用程序入口点。 在自定义活动中,覆盖字符串UnityPlayerActivity。updateunitycommandlineararguments (String cmdLine)方法。 在该方法中,将cmdLine…...

VMware虚拟机迁移到阿里云

VMware虚拟机迁移到阿里云是一个涉及多个步骤的过程,具体如下: 使用阿里云的服务器迁移中心(SMC)进行P2V或V2V迁移。如果是小型应用,可以通过制作镜像文件然后上传至阿里云OSS,并基于该镜像创建ECS实例。对…...

2025年1月22日(什么是扫频)

扫频(Sweep Frequency)是一种信号处理技术,通常用于系统识别、频率响应分析和特性测试。它通过发送一个频率逐渐变化的信号(通常是正弦波或线性调频信号)来激励系统,然后测量系统的响应。这种方法可以帮助我…...

前端开发中的模拟后端与MVVM架构实践[特殊字符][特殊字符][特殊字符]

平时,后端可能不能及时给接口给前端进行数据调用和读取。这时候,前端想到进行模拟后端接口。本文将介绍如何通过vite-plugin-mock插件模拟后端接口,并探讨MVVM架构在前端开发中的应用。此外,我们还将讨论Vue2与Vue3的区别&#xf…...

Win10系统部署RabbitMQ Server

文章目录 版本说明依赖安装添加Erlang环境变量验证Erlang安装 RabbitMQ Server安装解压启动查看RabbitMQ插件安装rabbitmq_management插件再次启动设置RabbitMQ为系统服务 版本说明 ErlangRabbitMQ27.24.0.5 可以在Erlang官网和RabbitMQ官网下载安装包,安装已下载…...

Java web与Java中的Servlet

一。前言 Java语言大多用于开发web系统的后端,也就是我们是的B/S架构。通过浏览器一个URL去访问系统的后端资源和逻辑。 当我在代码里看到这个类HttpServletRequest 时 让我想到了Servlet,Servlet看上去多么像是Java的一个普通类,但是它确实…...

Web开发 -前端部分-CSS3新特性

1 CSS概述 2 CSS3私有前缀 3 CSS3的长度单位 代码实现&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…...

2025年PHP面试宝典,技术总结。

面试是进入职场的第一道坎&#xff0c;因为我本身学校太一般的问题在面试中遇到了各种不爽&#xff0c;和那些高学历的相比自己真是信心大跌。我面试的方向是php开发工程师&#xff0c;主要做网站后台、APP接口等。下面是我这段时间总结的面试方面的常考常问的知识点&#xff0…...

idea plugin插件开发——入门级教程(IntelliJ IDEA Plugin)

手打不易&#xff0c;如果转摘&#xff0c;请注明出处&#xff01; 注明原文&#xff1a;idea plugin插件开发——入门级教程&#xff08;IntelliJ IDEA Plugin&#xff09;-CSDN博客 目录 前言 官方 官方文档 代码示例 开发前必读 Intellij、Gradle、JDK 版本关系 plu…...

node.js 文件操作

在 Node.js 中&#xff0c;文件操作主要通过内置的 fs&#xff08;File System&#xff09;模块来实现。 1. 读取文件 const fs require("fs");// 异步读取文件fs.readFile("example.txt", "utf8", (err, data) > {if (err) {console.erro…...