Web API 核心 JWT 身份验证不工作
Web API Core JWT Authentication is not working
我是在 .NET 中编写 Web API 的新手。我写了这个 API ,它正常工作但后来我添加了 JWT
身份验证,现在当我提供正确的用户名和密码时,我得到一个身份验证承载令牌,我添加到 swagger UI 但现在当我尝试访问任何其他端点时,我得到这个 401
未授权状态。我无法理解为什么。我也用 Postman 尝试过这个,但响应相同。
这是我的Program.cs
using System.Text;
using Comply_Api_DotNet.Database;
using Comply_Api_DotNet.Repository;
using Comply_Api_DotNet.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<IUsersDb, UsersDb>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
});
// ADD JWT Authentication
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
var key = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]);
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidAudience = builder.Configuration["JWT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key)
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
她是我的控制器。
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IAuthenticationService _authenticationService;
private readonly IUsersDb _usersDb;
public UsersController(IAuthenticationService authenticationService, IUsersDb usersDb)
{
_authenticationService = authenticationService;
_usersDb = usersDb;
}
[AllowAnonymous]
[HttpPost]
[Route("authenticate")]
public IActionResult Authenticate(User user)
{
var token = _authenticationService.Authenticate(user);
if (token == null)
{
return Unauthorized();
}
return Ok(token);
}
// GET api/<UsersController>/5
[HttpGet]
public IEnumerable<User> Get(long id)
{
var usersFound = _usersDb.GetAllUsers(id);
return usersFound;
}
// POST api/<UsersController>
[HttpPost]
public User Post([FromBody] User user)
{
var userAdded = _usersDb.AddNewUser(user);
return userAdded;
}
// PUT api/<UsersController>/5
[HttpPut("{id:long}")]
public void Put(long id, [FromBody] User user)
{
throw new NotImplementedException();
}
[HttpDelete("{id:long}")]
public bool Delete(long id)
{
return _usersDb.DeleteUser(id);
}
} // end of class
appsettings.Json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JWT": {
"Key": "fc746b61cde4f6665d3f9791446cd5395661860c0075a905ed9810b7391af467",
"Issuer": "Comply",
"Audience": "comply"
}
}
更新:身份验证服务
public class AuthenticationService : IAuthenticationService
{
private readonly IConfiguration _configuration;
private readonly IUsersDb _usersDb;
public AuthenticationService(IConfiguration configuration, IUsersDb usersDb)
{
_configuration = configuration;
_usersDb = usersDb;
}
public AuthenticationToken? Authenticate(User user)
{
var foundUser = _usersDb.GetAllUsers(0)
.FirstOrDefault(x => x.Name == user.Name && x.Password == user.Password);
if (foundUser == null)
{
return null;
}
//If user found then generate JWT
return CreateAuthenticationToken(foundUser);
}
private AuthenticationToken CreateAuthenticationToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenKey = Encoding.UTF8.GetBytes(_configuration["JWT:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.Name, user.Name),
}),
Expires = DateTime.UtcNow.AddMinutes(10),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey),
SecurityAlgorithms.HmacSha256Signature),
Issuer = _configuration["JWT:Issuer"],
Audience = _configuration["JWT:Audience"],
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new AuthenticationToken()
{
Token = tokenHandler.WriteToken(token),
};
}
} //end of class
问题出在这里 Type = SecuritySchemeType.ApiKey,
您将安全方案类型指定为 apiKey
。您需要将其替换为 Type = SecuritySchemeType.Http,
。所以,您的 OpenApiSecurityScheme
现在应该看起来像这样。
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
});
大摇大摆Ui,Token前要加Bearer
。参考:Bearer Authentication
Authorization: Bearer <token>
正如您在 Postman Headers 中看到的那样,它有一个 Bearer
。
我是在 .NET 中编写 Web API 的新手。我写了这个 API ,它正常工作但后来我添加了 JWT
身份验证,现在当我提供正确的用户名和密码时,我得到一个身份验证承载令牌,我添加到 swagger UI 但现在当我尝试访问任何其他端点时,我得到这个 401
未授权状态。我无法理解为什么。我也用 Postman 尝试过这个,但响应相同。
这是我的Program.cs
using System.Text;
using Comply_Api_DotNet.Database;
using Comply_Api_DotNet.Repository;
using Comply_Api_DotNet.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<IUsersDb, UsersDb>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
});
// ADD JWT Authentication
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
var key = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]);
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidAudience = builder.Configuration["JWT:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(key)
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
她是我的控制器。
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IAuthenticationService _authenticationService;
private readonly IUsersDb _usersDb;
public UsersController(IAuthenticationService authenticationService, IUsersDb usersDb)
{
_authenticationService = authenticationService;
_usersDb = usersDb;
}
[AllowAnonymous]
[HttpPost]
[Route("authenticate")]
public IActionResult Authenticate(User user)
{
var token = _authenticationService.Authenticate(user);
if (token == null)
{
return Unauthorized();
}
return Ok(token);
}
// GET api/<UsersController>/5
[HttpGet]
public IEnumerable<User> Get(long id)
{
var usersFound = _usersDb.GetAllUsers(id);
return usersFound;
}
// POST api/<UsersController>
[HttpPost]
public User Post([FromBody] User user)
{
var userAdded = _usersDb.AddNewUser(user);
return userAdded;
}
// PUT api/<UsersController>/5
[HttpPut("{id:long}")]
public void Put(long id, [FromBody] User user)
{
throw new NotImplementedException();
}
[HttpDelete("{id:long}")]
public bool Delete(long id)
{
return _usersDb.DeleteUser(id);
}
} // end of class
appsettings.Json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JWT": {
"Key": "fc746b61cde4f6665d3f9791446cd5395661860c0075a905ed9810b7391af467",
"Issuer": "Comply",
"Audience": "comply"
}
}
更新:身份验证服务
public class AuthenticationService : IAuthenticationService
{
private readonly IConfiguration _configuration;
private readonly IUsersDb _usersDb;
public AuthenticationService(IConfiguration configuration, IUsersDb usersDb)
{
_configuration = configuration;
_usersDb = usersDb;
}
public AuthenticationToken? Authenticate(User user)
{
var foundUser = _usersDb.GetAllUsers(0)
.FirstOrDefault(x => x.Name == user.Name && x.Password == user.Password);
if (foundUser == null)
{
return null;
}
//If user found then generate JWT
return CreateAuthenticationToken(foundUser);
}
private AuthenticationToken CreateAuthenticationToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenKey = Encoding.UTF8.GetBytes(_configuration["JWT:Key"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.Name, user.Name),
}),
Expires = DateTime.UtcNow.AddMinutes(10),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey),
SecurityAlgorithms.HmacSha256Signature),
Issuer = _configuration["JWT:Issuer"],
Audience = _configuration["JWT:Audience"],
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return new AuthenticationToken()
{
Token = tokenHandler.WriteToken(token),
};
}
} //end of class
问题出在这里 Type = SecuritySchemeType.ApiKey,
您将安全方案类型指定为 apiKey
。您需要将其替换为 Type = SecuritySchemeType.Http,
。所以,您的 OpenApiSecurityScheme
现在应该看起来像这样。
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"Please provide authorization token to access restricted features.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
});
大摇大摆Ui,Token前要加Bearer
。参考:Bearer Authentication
Authorization: Bearer <token>
正如您在 Postman Headers 中看到的那样,它有一个 Bearer
。