目录

ASP.NET Core 中的 JWT 鉴权实现

在当今的软件开发中,安全性和用户认证是至关重要的方面。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 应用中身份验证的理想选择。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
Asthenia041244 分钟前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04121 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom1 小时前
快速开始使用 n8n
后端·面试·github
JavaGuide2 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz9652 小时前
qemu 网络使用基础
后端
Asthenia04122 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04122 小时前
Spring 启动流程:比喻表达
后端
Asthenia04123 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua3 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫