我创建了一个使用JWT令牌进行授权的Web API,该令牌基于基于角色的策略(根据此文章)。用户登录后会生成一个用于授权的令牌。我成功生成了令牌,但当我开始使用它来访问受限API操作时,它无法正常工作,并不断给出401 HTTP错误(考虑到动作调用不触发,我甚至无法进行调试)。我做错了什么?。
类:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<ICountriesService, CountriesService>();
services.AddScoped<ICompanyService, CompanyService>();
services.AddScoped<IPlaneServices, PlaneService>();
services.AddScoped<IPlaneTypeService, PlaneTypeService>();
services.AddScoped<ICitiesService, CitiesService>();
services.AddScoped<IAirfieldService, AirfieldService>();
services.AddScoped<ITicketTypeService, TicketTypeService>();
services.AddScoped<IFlightService, FlightService>();
services.AddScoped<ILuxuryService, LuxuryService>();
services.AddScoped<IUserService, UserService>();
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
Enter 'Bearer' [space] and then your token in the text input below.
\r\n\r\nExample: 'Bearer 12345abcdef'",
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>()
}
});
// var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
//var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
//c.IncludeXmlComments(xmlPath);
});
services.AddAutoMapper(cfg => cfg.AddProfile<Mapper.Mapper>(),
AppDomain.CurrentDomain.GetAssemblies());
services.AddDbContext<FlightMasterContext>();
services.AddCors();
var secret = Configuration.GetValue<string>(
"AppSettings:Secret");
var key = Encoding.ASCII.GetBytes(secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.RoutePrefix = string.Empty;
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
控制器:[Route("api/[controller]")]
[ApiController]
[Authorize]
public class AaTestController : ControllerBase
{
private FlightMasterContext db { get; set; }
private IUserService _userService;
public AaTestController(FlightMasterContext db, IUserService userService)
{
this.db = db;
_userService = userService;
}
[AllowAnonymous]
[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody]AuthenticateModel model)
{
var user = _userService.Authenticate(model.Username, model.Password);
if (user == null)
return BadRequest(new { message = "Username or password is incorrect" });
return Ok(user);
}
//DOESNT TRIGGER
[Authorize(Roles = Role.Admin)]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
// only allow admins to access other user records
var currentUserId = int.Parse(User.Identity.Name);
if (id != currentUserId && !User.IsInRole(Role.Admin))
return Forbid();
var user = _userService.GetById(id);
if (user == null)
return NotFound();
return Ok(user);
}
}
用于认证和授权的服务:
public interface IUserService
{
User Authenticate(string username, string password);
IEnumerable<User> GetAll();
User GetById(int id);
}
public class UserService : IUserService
{
// users hardcoded for simplicity, store in a db with hashed passwords in production applications
private List<User> _users = new List<User>
{
new User { Id = 1, FirstName = "Admin", LastName = "User", Username = "admin", Password = "admin", Role = Role.Admin },
new User { Id = 2, FirstName = "Normal", LastName = "User", Username = "user", Password = "user", Role = Role.User }
};
public User Authenticate(string username, string password)
{
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
var secret = "THIS IS Ughjgjhgjhghgighiizgzigiz";
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString()),
new Claim(ClaimTypes.Role, user.Role)
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
user.Token = tokenHandler.WriteToken(token);
return user.WithoutPassword();
}
public IEnumerable<User> GetAll()
{
return _users.WithoutPasswords();
}
public User GetById(int id)
{
var user = _users.FirstOrDefault(x => x.Id == id);
return user.WithoutPassword();
}
}