我正在开发一个具有分层架构的 ASP.NET Core 8.0 Web API 项目。我在身份验证和注册端点的 JWT 验证过程中遇到了问题。
在我的
program.cs
中,我设置了以下令牌验证参数:
var tokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = jwtSettings.ValidAudience,
ValidIssuer = jwtSettings.ValidIssuer,
IssuerSigningKey = new
SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret)),
ClockSkew = jwtSettings.TokenLifetime
};
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = tokenValidationParameters;
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["authorization"];
return Task.CompletedTask;
}
};
});
builder.Services.AddSingleton(tokenValidationParameters);
对于令牌生成,我使用以下方法:
public async Task<Response<RefreshTokenDto>> GenerateAuthResultForCustomAsync(Customer
customer)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret);
var userRoles = await _userManager.GetRolesAsync(customer);
var authClaims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, customer.Email),
new Claim(JwtRegisteredClaimNames.Jti,
Guid.NewGuid().ToString()),
new Claim("customerId", customer.Id),
new Claim("firstName", customer.FirstName),
new Claim("lastName", customer.LastName),
new Claim("countryId", customer.CountryId.ToString()),
new Claim("phoneNumber", customer.PhoneNumber),
new Claim("userName", customer.UserName)
};
authClaims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));
var tokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(authClaims),
Issuer = _jwtSettings.ValidIssuer,
Audience = _jwtSettings.ValidAudience,
Expires = _dateTimeProvider.Now.Add(_jwtSettings.TokenLifetime).UtcDateTime,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
}
最后验证token的方法是:
public class PrincipalTokenService : IPrincipalTokenService
{
private readonly TokenValidationParameters _tokenValidationParameters;
public PrincipalTokenService(TokenValidationParameters tokenValidationParameters)
{
_tokenValidationParameters = tokenValidationParameters;
}
public ClaimsPrincipal GetPrincipalFromToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
var handler = tokenHandler.ValidateToken(
token,
_tokenValidationParameters,
out var validatedToken);
return !IsJwtWithValid(validatedToken) ? null : handler;
}
catch
{
return null;
}
}
}
执行
GetPrincipalFromToken
时,出现以下错误:
Microsoft.IdentityModel.Tokens.SecurityTokenNoExpirationException:'IDX10225:生命周期验证失败。令牌缺少到期时间。代币类型: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'
但是,从令牌中删除到期日期并在配置中禁用其验证会导致发行者以及随后的受众出现类似的问题。尽管经过了这些验证,生成的令牌仍经 jwt.io 确认有效。
整个令牌生成和验证逻辑是从 ASP.NET Core 6.0 中的一个工作项目移植的,该项目已经运行了 3 年多,没有出现任何问题。服务器环境和配置没有变化,说明不是代码本身的问题。
我怀疑问题可能与软件包版本有关。以下是8.0项目中使用的相关包:
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3">
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.4.1 />
还有其他人在 ASP.NET Core 8.0 中遇到过类似的 JWT 验证问题吗?任何见解或建议将不胜感激。
从 ASP.NET Core 8.0 开始, Microsoft.IdentityModel.JsonWebTokens 类,它也派生自 SecurityToken,实现SecurityToken属性,
尝试一下推荐的操作:
将该属性向下转换为 JsonWebToken:
service.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => {
options.Events.OnTokenValidated = (context) => {
// Replace your cast to JwtSecurityToken.
JsonWebToken token = context.SecurityToken as JsonWebToken;
// Do something ...
};
});
您可以查看安全令牌了解更多信息。