一、新建net9.0项目WebApplication1,安装包
<ItemGroup><PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" /><PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" /></ItemGroup>
在线生成TOKEN:JWT在线解码/编码工具 - 解析、验证、生成JSON Web Token
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;
using System.Text.Json;var builder = WebApplication.CreateBuilder(args);// 配置 JWT 认证
var secretKey = "Tx7S/FjYAnh3LDpyOcysrZ0K6e2cWlIX4p8/X8xV2U0vqY4kbZ4EZFI8s0qc35T5Z80RbTkc0F/JE6UnQzwIcw==";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{ // 显示详细的 PII 错误(仅限开发环境!)options.IncludeErrorDetails = true; // 👈 返回错误详情options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)),ValidateIssuer = false,ValidateAudience = false,ValidateLifetime = true,ClockSkew = TimeSpan.Zero};// 捕获 JWT 验证事件options.Events = new JwtBearerEvents{OnAuthenticationFailed = context =>{// 捕获验证失败的原因Console.WriteLine($"JWT 验证失败: {context.Exception.Message}");// 可以在这里返回自定义错误信息(但生产环境不建议返回详细错误)context.Response.StatusCode = 401;context.Response.ContentType = "application/json";var errorMessage = new{error = "Unauthorized",message = context.Exception.Message // 返回具体错误信息};return context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage));},OnChallenge = context =>{// 当请求未提供 token 时触发Console.WriteLine("请求未提供 JWT Token");return Task.CompletedTask;},OnTokenValidated = context =>{// Token 验证成功时触发Console.WriteLine("JWT 验证成功");return Task.CompletedTask;}};});// 配置 Swagger
builder.Services.AddSwaggerGen(c =>
{c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT Auth API", Version = "v1" });// 添加 JWT 认证支持到 Swaggerc.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme{Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",Name = "Authorization",In = ParameterLocation.Header,Type = SecuritySchemeType.ApiKey,Scheme = "Bearer"});c.AddSecurityRequirement(new OpenApiSecurityRequirement{{new OpenApiSecurityScheme{Reference = new OpenApiReference{Type = ReferenceType.SecurityScheme,Id = "Bearer"}},Array.Empty<string>()}});
}); builder.Services.AddCors(options =>
{options.AddPolicy("AllowAll", policy =>{policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); // 必须允许 Authorization 头});
});// 添加服务到容器
builder.Services.AddControllers();var app = builder.Build();// 配置 HTTP 请求管道
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI(c =>{c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT Auth API v1");});
}app.UseCors("AllowAll");app.UseAuthentication(); // 必须在 UseAuthorization 之前
app.UseAuthorization();app.MapControllers();app.Run();
获取token:
AuthController
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;namespace JwtAuthApi.Controllers;[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{private readonly IConfiguration _configuration;public AuthController(IConfiguration configuration){_configuration = configuration;}[HttpPost("login")][AllowAnonymous]public IActionResult Login([FromBody] LoginModel login){// 这里应该有实际的用户验证逻辑// 这里只是示例,直接接受任何用户名/密码if (string.IsNullOrEmpty(login.Username)){return BadRequest("Username is required");}// 创建 tokenvar tokenHandler = new JwtSecurityTokenHandler();var key = Encoding.UTF8.GetBytes("Tx7S/FjYAnh3LDpyOcysrZ0K6e2cWlIX4p8/X8xV2U0vqY4kbZ4EZFI8s0qc35T5Z80RbTkc0F/JE6UnQzwIcw==");var tokenDescriptor = new SecurityTokenDescriptor{Subject = new ClaimsIdentity(new[]{new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()),new Claim(ClaimTypes.Name, login.Username)}),Expires = DateTime.UtcNow.AddHours(1),SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)};var token = tokenHandler.CreateToken(tokenDescriptor);var tokenString = tokenHandler.WriteToken(token);return Ok(new{Token = tokenString,ExpiresIn = (int)TimeSpan.FromHours(1).TotalSeconds});}
}public class LoginModel
{public string Username { get; set; }public string Password { get; set; }
}
WeatherForecastController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;namespace JwtAuthApi.Controllers;[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{private static readonly string[] Summaries = new[]{"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"};[HttpGet][Authorize] // 需要认证public IEnumerable<WeatherForecast> Get(){// 可以从 User 中获取 JWT 中的声明var userName = User.Identity?.Name;var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;Console.WriteLine($"User {userName} (ID: {userId}) accessed weather forecast");return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = Random.Shared.Next(-20, 55),Summary = Summaries[Random.Shared.Next(Summaries.Length)]}).ToArray();}
}public class WeatherForecast
{public DateTime Date { get; set; }public int TemperatureC { get; set; }public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);public string? Summary { get; set; }
}