一、新建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 认证支持到 Swagger
c.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");
}
// 创建 token
var 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; }
}