JWT 不记名令牌授权无效 asp net core web api
JWT bearer token Authorization not working asp net core web api
我创建了一个 Web api,它使用 JWT 令牌通过基于角色的策略进行授权(基于
this 条)。
用户登录生成一个令牌,用于授权。我成功生成了令牌,但是当我开始使用它来访问受限的 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();
}
}
应该以相反的顺序调用这些方法:
app.UseAuthentication();
app.UseAuthorization();
第一个中间件应该对用户进行身份验证,然后才是下一个 - 授权。不幸的是,并非所有 MS 文档都关注这个细节。
第一个中间件应该对用户进行身份验证,然后才是下一个 - 授权。不幸的是,并非所有 MS 文档都关注这个细节。
我创建了一个 Web api,它使用 JWT 令牌通过基于角色的策略进行授权(基于 this 条)。 用户登录生成一个令牌,用于授权。我成功生成了令牌,但是当我开始使用它来访问受限的 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();
}
}
应该以相反的顺序调用这些方法:
app.UseAuthentication();
app.UseAuthorization();
第一个中间件应该对用户进行身份验证,然后才是下一个 - 授权。不幸的是,并非所有 MS 文档都关注这个细节。
第一个中间件应该对用户进行身份验证,然后才是下一个 - 授权。不幸的是,并非所有 MS 文档都关注这个细节。