在当今的软件开发中,安全性和用户认证是至关重要的方面。JSON Web Token(JWT)作为一种流行的身份验证机制,因其简洁性和无状态特性而被广泛应用于各种应用中,尤其是在 ASP.NET Core 项目里。本文将详细介绍如何在 ASP.NET Core 应用中实现 JWT 鉴权,确保应用能够安全地验证用户身份并授权访问特定资源。
一、安装必要的 NuGet 包
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
这个包提供了 JWT 身份验证所需的所有功能,包括对 JWT 令牌的解析和验证。
二、配置 JWT 身份验证
在应用的配置文件 appsettings.json
中添加 JWT 相关的配置信息。这些配置包括密钥、发行者、受众和令牌的有效期等:
{"JwtSettings": {"Secret": "YourSecretKeyYourSecretKeyYourSecretKeyYourSecretKey", // 用于加密的密钥(应非常复杂)"Issuer": "YourAppName", // 发行者"Audience": "YourAppUsers", // 受众"ExpireMinutes": 120 // 令牌有效期}
}
在 Program.cs
文件中,需要配置 JWT 认证方案,以便 ASP.NET Core 知道如何处理 JWT 令牌:
var builder = WebApplication.CreateBuilder(args);var jwtSettings = builder.Configuration.GetSection("JwtSettings");builder.Services.AddAuthentication(options =>
{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = jwtSettings["Issuer"],ValidAudience = jwtSettings["Audience"],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"])),ClockSkew = TimeSpan.Zero // 默认的 5 分钟偏移时间};options.Events = new JwtBearerEvents
{ OnMessageReceived = context =>{var token = context.Request.Headers["Authorization"].ToString()?.Replace("Bearer ", "");if (!string.IsNullOrEmpty(token)){context.Token = token;}return Task.CompletedTask;},OnAuthenticationFailed = context =>{// 如果过期,把过期信息添加到头部if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)){context.Response.Headers.Append("Token-Expired", "true");}return Task.CompletedTask;}
};
});var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
配置了 JWT 身份验证的中间件,确保所有传入的请求都会被检查是否包含有效的 JWT 令牌。
JwtBearerEvents的订阅事件
// 1. **OnMessageReceived**:// -触发时机:当接收到一个身份验证请求。// -用途:用来处理接收到的原始身份验证消息,可以根据请求的具体情况来修改或取消身份验证过程。//2. * *OnTokenValidated * *:// -触发时机:在JWT被成功验证后触发。// -用途:用来处理已验证的token,例如,可以在这里添加额外的日志记录或执行一些安全检查。//3. * *OnAuthenticationFailed * *:// -触发时机:当身份验证失败时触发。// -用途:用来处理身份验证失败的情况,例如,记录失败原因、执行额外的错误处理逻辑等。//4. * *OnChallenge * *:// -触发时机:当需要向客户端发出一个挑战(例如,要求客户端提供凭据)时触发。// -用途:自定义挑战的响应,例如,修改返回给客户端的`401 Unauthorized`响应。//5. * *OnForbidden * *:// -触发时机:当授权失败时触发(即用户已通过身份验证,但没有足够的权限访问特定资源)。// -用途:自定义处理禁止访问的情况,例如,返回一个自定义的错误消息或执行其他逻辑。
三、生成 JWT Token
为了使用户能够登录并获取 JWT 令牌,需要创建一个控制器或服务来处理登录请求并生成 JWT 令牌。以下是一个简单的登录 API 示例:
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;[ApiController]
[Route("api/[controller]")]
publicclassAuthController : ControllerBase
{privatereadonly IConfiguration _configuration;public AuthController(IConfiguration configuration){_configuration = configuration;}[HttpPost("login")]public IActionResult Login([FromBody] LoginRequest loginRequest){// 假设已经验证了用户名和密码if (loginRequest.Username == "admin" && loginRequest.Password == "password"){var token = GenerateJwtToken(loginRequest.Username);return Ok(new { token });}return Unauthorized();}private string GenerateJwtToken(string username){var jwtSettings = _configuration.GetSection("JwtSettings");var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, username),new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())//添加更多的标识};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Secret"]));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(issuer: jwtSettings["Issuer"],audience: jwtSettings["Audience"],claims: claims,expires: DateTime.Now.AddMinutes(double.Parse(jwtSettings["ExpireMinutes"])),signingCredentials: creds);returnnew JwtSecurityTokenHandler().WriteToken(token);}
}publicclassLoginRequest
{publicstring Username { get; set; }publicstring Password { get; set; }
}
四、保护 API 路由
一旦有了 JWT 令牌生成机制,接下来需要保护 API 路由,确保只有携带有效 JWT 令牌的请求可以访问受保护的资源。
可以通过在控制器或操作方法上使用 [Authorize]
特性来实现:
[AllowAnonymous]
可跳过授权
[ApiController]
[Route("[controller]")]
[Authorize]
publicclassWeatherForecastController : ControllerBase
{[Authorize][HttpGet(Name = "GetWeatherForecast")]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();}
}
五、客户端请求
客户端在请求受保护的 API 时,必须在请求头中添加 Authorization
字段,格式为 Bearer <token>
:
GET /api/protected/data HTTP/1.1
Host: yourdomain.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
这样,服务器就能通过 JWT 中间件验证令牌的有效性,并允许或拒绝请求。
六、扩展:Swagger集成,传递验证
需要使用包Swashbuckle.AspNetCore
builder.Services.AddSwaggerGen(options => options.SwaggerTokenConfigure());
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}
SwaggerConfiguration将token保存到swagger并传递到后台
public staticclassSwaggerConfiguration
{public static void SwaggerTokenConfigure(this SwaggerGenOptions options){options.SwaggerDoc("v1", new OpenApiInfo{Title = "JWT Auth API",Version = "v1",Description = "A sample API for JWT Authentication"});options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme{Description = "JWT Authorization header using the Bearer scheme.",Name = "Authorization",In = ParameterLocation.Header,Type = SecuritySchemeType.Http,Scheme = "Bearer",BearerFormat = "JWT"});options.AddSecurityRequirement(new OpenApiSecurityRequirement{{new OpenApiSecurityScheme{Reference = new OpenApiReference{Type = ReferenceType.SecurityScheme,Id = "Bearer"}},Array.Empty<string>()}});}
}
有关swagger更多的详细配置,请参考
https://mp.weixin.qq.com/s/Xke2EyUHuxR_RdHbSXP5Ew
分组:https://mp.weixin.qq.com/s/Ut9leANrq4pJMgOQFmVkqg
七、扩展:获取身份验证信息
老规矩,先注册
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddTransient<IAppUser, AppUser>();
存放用户信息的接口和实现类
public interfaceIAppUser{string Username { get; }string HeaderToken { get; }}publicclassAppUser : IAppUser{privatereadonly HttpContext _httpContext;public AppUser(IHttpContextAccessor httpContextAccessor){_httpContext = httpContextAccessor?.HttpContext;}publicstring Username { get => _httpContext?.User.Claims.FirstOrDefault(s => s.Type == "Username")?.Value.ToString()??""; }publicstring HeaderToken{get{if (_httpContext.Request.Headers.ContainsKey("Authorization")){return _httpContext?.Request.Headers["Authorization"].ToString().Replace("Bearer ", "").Trim();}returnstring.Empty;}}}
直接使用
private readonly IAppUser _appUser ;public AuthController(IConfiguration configuration, IAppUser appUser){_configuration = configuration;_appUser=appUser;}[HttpGet]public IActionResult Getuserinfo(){return Ok(new { _appUser.Username, _appUser.HeaderToken });}
可以成功获取到用户存放的token信息,不过现在还有一个弊端,就是每次都需要构造注入才可以过去用户信息,是比较麻烦的,其实我们可以通过封装一下,将AppUser存放到App全局配置里面直接静态获取,这样在需要获取用户信息的时候,就不需要每次都构造注入了
等后面有机会出优化和封装教程,或者直接参考:https://gitee.com/Pridejoy/MalusAdmin
八、可选:角色授权
如果应用需要基于角色进行授权,可以在生成 JWT 令牌时添加角色信息:
private string GenerateJwtToken(string username)
{var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, username),new Claim(ClaimTypes.Role, "Admin"), // 添加角色new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())};// 剩余代码与前面一致
}
然后,可以使用 [Authorize(Roles = "Admin")]
特性来限定只有特定角色的用户才能访问:
[Authorize(Roles = "Admin")]
[HttpGet("admin-data")]
public IActionResult GetAdminData()
{return Ok(new { message = "This is admin data" });
}
总结
通过以上操作,就可以在 ASP.NET Core 应用中实现 JWT 鉴权,确保你的应用能够安全地验证用户身份并授权访问特定资源。JWT 的无状态特性和灵活性使其成为现代 Web 应用中身份验证的理想选择。