代码是.NET 8,使用Microsoft.AspNetCore.Authentication.JwtBearer身份验证。
这是我第一次在 REST 服务器上实现 OAUTH,所以我怀疑这可能是一个 nubie 错误。你能找出为什么会发生这种情况吗?
症状:我执行登录功能,它创建一个不记名票证并将其返回。然后,我将不记名票证放入数据请求端点,它返回时什么也没有(在 CURL 上),或者在 SoapUI 上显示“不记名票错误 = 无效令牌”。
我已确保客户端请求中的受众与 JWT 票证中发出的受众相匹配,并且我非常小心地复制不记名令牌以包含所有字符,没有多余的字符。
如果我删除控制器上的 [Authorize] 属性,调用数据请求端点会返回很好的数据,所以我知道该方法有效。
当我将 JWT 与 REST 服务器集成时,我遵循了以下步骤:
...
//=== 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();
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 });
}
}
[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;
}
}
}
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
请将
app.UseAuthorization();
放在app.UseAuthentication();
后面。
我用下面的代码进行了测试,它给我带来了 401 错误,而将
UseAuthorization
放在 UseAuthentication
后面会返回 200。
app.UseAuthorization();
app.UseAuthentication();
//app.UseAuthorization();
我们这里有文件提到了
UseCors、UseAuthentication 和 UseAuthorization 必须出现在 显示顺序。
您检查日志是否有任何具体错误?也许有关于请求出了什么问题的线索。
创建令牌时,您手动设置
iat
声明,但将其设置为字符串。该声明应该具有时间戳的数值。您必须手动设置索赔吗?不是图书馆添加的吗?如果没有,请确保在那里添加数字时间戳,而不是字符串(与 exp
声明的值进行比较)。我的猜测是验证由于该值而失败。
作为旁注,请注意以下几点: