Web Api 返回404而不是401状态码。

4
我有一个.NET 6的Web API项目。在这个项目中,我有一个认证控制器和一个根据用户名和密码进行身份验证的端点。
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IdbContext _dbContext;

    public AuthenticationController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IdbContext _dbContext)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _dbContext = dbContext;
    }

    [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> PasswordSignInAsync([FromBody] LoginRequest loginRequest)
    {         
        if (ModelState.IsValid)
        {
            var result = await _signInManager.PasswordSignInAsync(loginRequest.Username, loginRequest.Password, isPersistent: false, lockoutOnFailure: false);
            if (result.Succeeded)
            {
                var armsUser = _dbContext.Users
                    .FirstOrDefault(u => u.Email == loginRequest.Username);

                if (armsUser == null) {
                    await _signInManager.SignOutAsync();
                    return BadRequest();
                }

                ApplicationUser user = await _userManager.FindByEmailAsync(loginRequest.Username);

                if (user == null)
                {
                    return NotFound($"Unable to load user with username '{loginRequest.Username}'.");
                }

                user.LastSignInDate = DateTime.Now;

                await _userManager.UpdateAsync(user);

                await _signInManager.RefreshSignInAsync(user);

                return Ok("Authenticated!");
            }
            await _signInManager.SignOutAsync();
            return BadRequest("Invalid username or password.");
        }
        return BadRequest(ModelState);
    }
}

这是受保护的资源。

[Route("api/[controller]")]
[ApiController]
public class MarketingEventController : ControllerBase
{        
    private readonly IMediator _mediator;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="mediator"></param>
    public MarketingEventController(IMediator mediator)
    {     
        _mediator = mediator;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public async Task<ActionResult> GetLatest()
    {
        try
        {
            var query = new GetLatestQuery();
            var result = await _mediator.Send(query);
            return Ok(result);

        }
        catch (Exception ex)
        {
            return Problem(ex.Message);
        }
    }
}

和一些相关的代码在 program.cs

...

app.UseAuthentication();
app.UseAuthorization();


app.MapControllers().RequireAuthorization();

如果我已经通过身份验证,就会得到预期的200状态码。但是,如果我没有通过身份验证并调用/api/MarketingEvent,为什么我会得到404状态码而不是401状态码?即使我在营销控制器上使用[Authorize]修饰符,它的行为仍然相同。

enter image description here

enter image description here


1
可能是中间件的魔法,请尝试一下 https://dev59.com/mbfna4cB1Zd3GeqPny6l - user3682728
2
请问您能否展示一下如何配置身份验证和授权,即在启动时添加AddAuthentication和AddAuthorization调用? 此外,RequireAuthorization注册了一个公约,将[Authorize]应用于所有控制器,因此手动执行不应有任何区别。 另外,我想询问一下框架 - 您提到的是.NET 6,标签是ASP.NET。我推测这与ASP.NET Core有关,而不是ASP.NET 4。 - Zdeněk Jelínek
1
你有检查过这个问题吗?https://dev59.com/hsLra4cB1Zd3GeqPBxhr - Tasos K.
3个回答

0

DefaultChallengeScheme 可能会重定向到不存在的页面,导致 404 错误。

尝试将默认的 challenge 设置为 Jwt schema,以返回未授权信息。

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
       options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
       options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddIdentityServerAuthentication(options =>
    {
      options.Authority = "http://localhost:60415";
      options.ApiName = "mCareApi";
      options.RequireHttpsMetadata = false;
    });
}

1
我也认为身份验证出了问题。可能存在一个内部重定向到不存在的登录页面。在一些旧项目中,我使用https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.cookies.cookieauthenticationoptions.loginpath?view=aspnetcore-6.0#microsoft-aspnetcore-authentication-cookies-cookieauthenticationoptions-loginpath来指定路径。因此,重定向到登录页面或错误页面可能导致404错误。 - citronas

0

我不知道你是如何注册你的Swagger服务的,但尝试使用类似以下的方式:

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })

此外,使用以下属性装饰您的端点

[ProducesResponseType(StatusCodes.Status401Unauthorized)]

另外,如果你想避免将此应用于所有操作方法,你有两个选择:

  1. 你可以创建一个基础控制器并使用该属性进行装饰。然后,使所有的控制器都继承自该基础控制器。
  2. 你可以在你的program.cs中添加一个全局过滤器。类似这样的东西
services.AddMvc(options =>
{
    options.Filters.Add(new Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(typeof(ApiError), 400));
    options.Filters.Add(new Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(typeof(ApiError), 401));
});

0
在每个 GET api/MarketingEvent/ 请求中,ASP.NET Core Routing 将尝试将传入的请求与控制器中的 Action 方法匹配。在这种情况下,您没有指定任何 Action 方法,因此它将查找默认的 Action 方法,即 Index。这就是为什么它返回 404 Not Found 的原因。
我假设您想调用 GetLatest action 方法,因此您需要使用一个空的 [Route("")] 属性来装饰 GetLatest action 方法。您的方法将如下所示:
/// <summary>
/// 
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult> GetLatest()
{
    try
    {
        var query = new GetLatestQuery();
        var result = await _mediator.Send(query);
        return Ok(result);

    }
    catch (Exception ex)
    {
        return Problem(ex.Message);
    }
}

如果我已经通过身份验证,那么我会得到预期的200状态码。请阅读这行内容。 - MD. RAKIB HASAN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接