You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7 Web API:通过请求头传递JWT实现接口访问授权

问题描述

目标: 从前端向后端传递JWT,以访问TopicController的接口。

我有一个连接React.ts前端的.NET 7 Web API,目前已将包含身份角色声明的JWT发送到前端并能成功接收,但后端处理请求时无法正常获取API访问权限。

网络请求中,目标接口返回两次204响应、两次302响应,随后出现两次来自.NET生成的登录检查接口的405响应。

在Swagger中授权功能正常,登录后无需在授权区域传递JWT即可访问接口。

React中的API调用:

const getTopics = () => {
    const url = API_ROUTES.TOPICS_GET_ALL_TOPICS;
    fetch(url, {
      method: "GET",
      headers: {
        'Authorization': `bearer ${jwt}`
      }
    })
    .then(res => res.json())
    .then(topicsFromServer => {
      setTopics(topicsFromServer);
    })
    .catch((error => {
      console.error(error);
    }))
  }

认证控制器中的登录方法(正确使用JWT后将重构):

[HttpPost]
[Route("/account/login")]
public async Task<IActionResult> Login(LoginDTO userLogin)
{
    Microsoft.AspNetCore.Identity.SignInResult result = await _signInManager.PasswordSignInAsync(userLogin.Email, userLogin.Password, true, false);
    if (!result.Succeeded)
        return BadRequest(400);

    ApplicationUser user = await _userManager.FindByEmailAsync(userLogin.Email);
    string token = await CreateToken(user);

    LoginResult resultResponse = new LoginResult
    {
        succeeded = true,
        Data = new List<object> { token
        }
    };

    return Ok(resultResponse);
}

认证控制器中的创建Token方法:

private async Task<string> CreateToken(ApplicationUser user)
{
    IList<Claim> claims = await _userManager.GetClaimsAsync(user);

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
        _configuration.GetSection("AppSettings:Token").Value!));

    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);

    var token = new JwtSecurityToken(
            claims: claims,
            expires: DateTime.Now.AddDays(1),
            signingCredentials: creds
        );

    var jwt = new JwtSecurityTokenHandler().WriteToken(token);
    return jwt;
}

TopicController(仅登录且拥有User角色的用户可访问):

namespace SdeResearch.Api.Controllers
{
    [ApiController]
    [Route("[controller]")]
    [Authorize(Roles = "User")]
    public class TopicController : ControllerBase
    {
        private readonly SdeResearchDbContext _db;

        public TopicController(SdeResearchDbContext db)
        {
            _db = db;
        }

        [HttpGet]
        [Route("/topic/get-all-topics")]
        public async Task<List<Topic>> GetTopicsAsync()
        {
            return await _db.Topics.ToListAsync();
        }
    }
}

Program.cs中的Cors策略:

builder.Services.AddCors(options =>
{
    options.AddPolicy("CORSPolicy", builder =>
    {
        builder
        .AllowAnyMethod()
        .AllowAnyHeader()
        .WithOrigins("http://localhost:3000");
    });
});

添加认证与授权配置:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateAudience = true,
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration.GetSection("AppSettings:Issuer").Value!,
            ValidAudience = builder.Configuration.GetSection("AppSettings:Audience").Value!,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                builder.Configuration.GetSection("AppSettings:Token").Value!)),
        };
    });

builder.Services.AddAuthorization();
问题排查与解决方案

1. 补全JWT的Issuer和Audience配置

你的CreateToken方法生成JWT时未设置发行方(Issuer)和受众(Audience),但认证配置中开启了这两项验证,会直接导致Token验证失败。修改CreateToken方法:

private async Task<string> CreateToken(ApplicationUser user)
{
    IList<Claim> claims = await _userManager.GetClaimsAsync(user);

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
        _configuration.GetSection("AppSettings:Token").Value!));

    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);

    var token = new JwtSecurityToken(
            issuer: _configuration.GetSection("AppSettings:Issuer").Value!,
            audience: _configuration.GetSection("AppSettings:Audience").Value!,
            claims: claims,
            expires: DateTime.Now.AddDays(1),
            signingCredentials: creds
        );

    var jwt = new JwtSecurityTokenHandler().WriteToken(token);
    return jwt;
}

2. 确保角色声明格式正确

ASP.NET Core的[Authorize(Roles = "User")]默认检查ClaimTypes.Role类型的声明,需确保Token中包含正确的角色声明。在CreateToken中添加角色获取逻辑:

// 在CreateToken方法中,获取用户角色并添加到claims集合
var roles = await _userManager.GetRolesAsync(user);
foreach (var role in roles)
{
    claims.Add(new Claim(ClaimTypes.Role, role));
}

3. 禁用未授权请求的自动跳转

出现302响应是因为.NET默认会将未授权请求重定向到登录页(Cookie认证行为),需在JWT配置中关闭此逻辑:

.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration.GetSection("AppSettings:Issuer").Value!,
        ValidAudience = builder.Configuration.GetSection("AppSettings:Audience").Value!,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
            builder.Configuration.GetSection("AppSettings:Token").Value!)),
    };
    // 禁用重定向,直接返回401未授权
    options.Events = new JwtBearerEvents
    {
        OnChallenge = context =>
        {
            context.HandleResponse();
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            return Task.CompletedTask;
        }
    };
});

4. 正确启用CORS中间件

确保CORS中间件在认证中间件之前启用,否则跨域请求会被拦截:

// Program.cs中间件管道顺序
app.UseCors("CORSPolicy");
app.UseAuthentication();
app.UseAuthorization();

5. 明确指定认证方案

为避免Cookie认证干扰JWT认证,可在[Authorize]特性中明确指定JWT方案:

[Authorize(Roles = "User", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

内容的提问来源于stack exchange,提问作者Morgan Bradford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 03:24:55