我目前正在学习here教程,到目前为止我已经能够成功生成JWT令牌。我创建了一个授权控制器(Dashboard
),但响应总是导致404未经授权,所以我评论它导致我的Get方法中抛出了“Sequence containing no matching element
”错误;意思是“id
”没有找到,但很明显,如附图中所示,“id”存在。最后,我尝试了User.Identity.IsAuthenticated
,但这总是导致false
。我究竟做错了什么?
DashboardController.cs:
namespace JwtTest.API.Controllers
{
// [Authorize(Policy = "ApiUser")]
[Route("api/[controller]/[action]")]
public class DashboardController : Controller
{
private readonly ClaimsPrincipal _caller;
private readonly ApplicationDbContext _appDbContext;
public DashboardController(UserManager<AppUser> userManager, ApplicationDbContext appDbContext, IHttpContextAccessor httpContextAccessor)
{
_caller = httpContextAccessor.HttpContext.User;
_appDbContext = appDbContext;
}
// GET api/dashboard/home
[HttpGet]
public async Task<IActionResult> Home()
{
var userId = _caller.Claims.Single(c => c.Type == "id");
var customer = await _appDbContext.Customers.Include(c => c.Identity).SingleAsync(c => c.Identity.Id == userId.Value);
return new OkObjectResult(new
{
Message = "This is secure API and user data!",
customer.Identity.FirstName,
customer.Identity.LastName,
customer.Identity.PictureUrl,
customer.Identity.FacebookId,
customer.Location,
customer.Locale,
customer.Gender
});
}
}
}
Startup.cs:
namespace JwtTest.API {
public class Startup {
private const string SecretKey = "iNivDmHLpUA223sqsfhqGbMRdRj1PVkH";
private readonly SymmetricSecurityKey _signingKey
= new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
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.AddDbContext<ApplicationDbContext>
(x => x.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvc();
// services.AddIdentity<AppUser, IdentityRole>()
// .AddEntityFrameworkStores<ApplicationDbContext>()
// .AddDefaultTokenProviders();
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
services.Configure<JwtIssuerOptions>(options => {
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions => {
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
var builder = services.AddIdentityCore<AppUser>(o => {
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
// api user claim policy
services.AddAuthorization(options => {
options.AddPolicy("ApiUser", policy =>
policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.AddAutoMapper();
services.AddMvc().AddFluentValidation(fv =>fv.RegisterValidatorsFromAssemblyContaining<Startup>());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
app.UseAuthentication();
app.UseMvc();
}
}
}
AuthController:
namespace JwtTest.API.Controllers {
[Route("api/[controller]")]
public class AuthController : Controller {
private readonly UserManager<AppUser> _userManager;
private readonly IJwtFactory _jwtFactory;
private readonly JwtIssuerOptions _jwtOptions;
public AuthController(UserManager<AppUser> userManager, IJwtFactory jwtFactory, IOptions<JwtIssuerOptions> jwtOptions) {
_jwtFactory = jwtFactory;
_jwtOptions = jwtOptions.Value;
_userManager = userManager;
}
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if(!ModelState.IsValid) return BadRequest(ModelState);
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if(identity == null)
return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password", ModelState));
var jwt = await Tokens.GenerateJwt(identity, _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings {
Formatting = Formatting.Indented
});
return new OkObjectResult(jwt);
}
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
{
if(string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
return await Task.FromResult<ClaimsIdentity>(null);
var userToVerify = await _userManager.FindByNameAsync(userName);
if(userToVerify == null) return await Task.FromResult<ClaimsIdentity>(null);
if(await _userManager.CheckPasswordAsync(userToVerify, password))
return await Task.FromResult(_jwtFactory.GenerateClaimsIdentity(userName, userToVerify.Id));
return await Task.FromResult<ClaimsIdentity>(null);
}
}
}
AccountsController:
namespace JwtTest.API.Controllers {
[Route("api/[controller]")]
public class AccountsController : Controller {
private readonly UserManager<AppUser> _userManager;
private readonly IMapper _mapper;
private readonly ApplicationDbContext _appDbContext;
public AccountsController(UserManager<AppUser> userManager, IMapper mapper, ApplicationDbContext appDbContext) {
_appDbContext = appDbContext;
_mapper = mapper;
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] RegistrationViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var userIdentity = _mapper.Map<AppUser>(model);
var result = await _userManager.CreateAsync(userIdentity, model.Password);
if(!result.Succeeded) return new BadRequestObjectResult(Errors.AddErrorsToModelState(result, ModelState));
await _appDbContext.Customers.AddAsync(new Customer {
IdentityId = userIdentity.Id,
Location = model.Location
});
await _appDbContext.SaveChangesAsync();
return new OkObjectResult("Account created");
}
}
}
我对此代码有同样的问题。
确保appsettings.json位于项目的主文件夹中。 (与startup.cs相同)。
由于存在未被导入的受众和发行者的设置。这导致身份验证失败。
那应该可以解决你的问题。