JWT Bearer Token 正在颁发,但端点身份验证失败

问题描述 投票:0回答:2

代码是.NET 8,使用Microsoft.AspNetCore.Authentication.JwtBearer身份验证。

这是我第一次在 REST 服务器上实现 OAUTH,所以我怀疑这可能是一个 nubie 错误。你能找出为什么会发生这种情况吗?

症状:我执行登录功能,它创建一个不记名票证并将其返回。然后,我将不记名票证放入数据请求端点,它返回时什么也没有(在 CURL 上),或者在 SoapUI 上显示“不记名票错误 = 无效令牌”。

我已确保客户端请求中的受众与 JWT 票证中发出的受众相匹配,并且我非常小心地复制不记名令牌以包含所有字符,没有多余的字符。

如果我删除控制器上的 [Authorize] 属性,调用数据请求端点会返回很好的数据,所以我知道该方法有效。

当我将 JWT 与 REST 服务器集成时,我遵循了以下步骤:

  1. 将集成添加到 Program.cs
...
//=== OTB Template
builder.Services.AddControllers();

//=== Added for OAUTH / JWT
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true,
        ValidateAudience = false,
        ValidIssuer = ServerUtility.GetConfigValue("Jwt:Issuer"),
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ServerUtility.GetConfigValue("Jwt:Key"))),
    };
});

...
app.UseHttpsRedirection();

app.UseAuthorization();

// Added for JWT
app.UseAuthentication();

app.MapControllers();
  1. 我的登录功能,在“TokenController.cs”中:
        public async Task<IActionResult> PostToken(UserModel APIUser)
        {
            try
            {
                if (APIUser is not null)
                {
                    var user = await GetUser(APIUser.apiUserId);
                    if (user is not null)
                    {
                        // Create a claim
                        var claims = new[]
                        {
                            new Claim(JwtRegisteredClaimNames.Sub, ServerUtility.GetConfigValue("Jwt:Subject")),
                            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                            new Claim(ClaimTypes.Name, user.apiUserName),
                            new Claim("UserId", user.apiUserId.ToString()),
                            new Claim("DisplayName", user.apiUserName),
                            new Claim("Email", $"{user.apiRecoveryEmail}"),
                        };
                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ServerUtility.GetConfigValue("Jwt:Key")));
                        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                        var token = new JwtSecurityToken(
                            ServerUtility.GetConfigValue("Jwt:Issuer"),
                            ServerUtility.GetConfigValue("Jwt:Audience"),
                            claims,
                            expires: DateTime.UtcNow.AddMinutes(10),
                            signingCredentials: credentials);
                        return Ok(new JwtSecurityTokenHandler().WriteToken(token));
                    }
                    else
                    {
                        return StatusCode(StatusCodes.Status404NotFound, new JsonObject() { ["code"] = 404, ["message"] = "Account not found" });
                    }
                }
                return StatusCode(StatusCodes.Status400BadRequest, new JsonObject() { ["code"] = 400, ["message"] = "Missing Account info in request" });
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, new JsonObject() { ["code"] = 500, ["message"] = ex.Message });
            }
        }
  1. 我的数据请求端点(不同的控制器):
    [Route("/api/[Controller]"), ApiController, Authorize]
    public class AccountsController : ControllerBase
    {
        private readonly IAccount _IAccount;

        public AccountsController(IAccount IAccount)
        {
            _IAccount = IAccount;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<AccountModel>>> GetAccountsAsync()
        {
            try
            {
                return await Task.FromResult(_IAccount.GetAccountDetails());
            }
            catch
            {
                throw;
            }
        }
    }
  1. 我的 CURL 请求(Windows):
curl -X POST -H "Content-Type: application/json" -d "{\"grant_type\":\"password\",\"apiUserId\":\"SystemAdministrator\",\"apiAPIKey\":\"the-appropriate-code\",\"apiAPISecret\":\"the-appropriate-code\",\"client_id\":\"GV_App\",\"client_secret\":\"your_client_secret\"}" https://localhost:7089/api/Token
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKV1RTZXJ2aWNlQWNjZXNzVG9rZW4iLCJqdGkiOiI1NjZlMmI0YS02ZGFkLTRkNDUtYWQ0My1lZWMyYTJjZTNmZGMiLCJpYXQiOiIyLzI3LzIwMjQgNzozMjozOSBQTSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJHYW1pVmVyc2UgU3lzdGVtIEFkbWluaXN0cmF0b3IiLCJVc2VySWQiOiJTeXN0ZW1BZG1pbmlzdHJhdG9yIiwiRGlzcGxheU5hbWUiOiJHYW1pVmVyc2UgU3lzdGVtIEFkbWluaXN0cmF0b3IiLCJFbWFpbCI6ImRhbkB0aGVwYXJhbGxlbHJldm9sdXRpb24uY29tIiwiZXhwIjoxNzA5MDYyOTU5LCJpc3MiOiJKV1RBdXRoZW50aWNhdGlvblNlcnZlciIsImF1ZCI6IkdWX0FwcCJ9.-RwUCP6brplGbHIW2npmPOaWHPQ5GSV1dLudWTpgfT8" https://localhost:7089/api/Accounts
rest .net-core oauth jwt asp.net-core-webapi
2个回答
0
投票

请将

app.UseAuthorization();
放在
app.UseAuthentication();
后面。

我用下面的代码进行了测试,它给我带来了 401 错误,而将

UseAuthorization
放在
UseAuthentication
后面会返回 200。

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

我们这里有文件提到了

UseCors、UseAuthentication 和 UseAuthorization 必须出现在 显示顺序。


0
投票

您检查日志是否有任何具体错误?也许有关于请求出了什么问题的线索。

创建令牌时,您手动设置

iat
声明,但将其设置为字符串。该声明应该具有时间戳的数值。您必须手动设置索赔吗?不是图书馆添加的吗?如果没有,请确保在那里添加数字时间戳,而不是字符串(与
exp
声明的值进行比较)。我的猜测是验证由于该值而失败。

作为旁注,请注意以下几点:

  • 您实际上没有使用 OAuth,您只是使用 JWT 作为访问令牌。 OAuth 是一个安全框架,其标准描述了如何从授权服务器获取令牌。您没有实施任何这些流程。 (如果您考虑实现资源所有者密码凭证流程,请不要这样做。这是一个遗留流程,添加到 OAuth 中只是为了更简单地与 OAuth 之前存在的解决方案集成。该流程很快就会从规范中删除在 OAuth 2.1 中)
  • 如果我理解正确的话,您有相同的后端来发行和验证令牌。在这种情况下,我认为使用 JWT 就有点矫枉过正了。我认为使用普通的旧 HTTP 会话和 cookie 会更简单。
© www.soinside.com 2019 - 2024. All rights reserved.