我已经开发了此API,并且正在尝试向其添加JWT身份验证。
我继承了AspNetCore UserManager来执行所有身份验证。
我成功注册用户,然后对注册用户进行身份验证。一世
按预期方式获取JWT令牌,但是当我尝试访问锁定的资源时,
它拒绝访问,仅返回404(未找到)错误消息。当我
检查我的调试会话,我看到它正在重定向到通常的AspNetCore
MVC Web应用程序身份验证路由,但找不到它。有什么可能
是?
这是我的startUp.cs
namespace ProjectManager.Web.API
{
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.Configure<IdentityOptions>(options =>
{
options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
options.User.RequireUniqueEmail = false;
options.Password.RequireUppercase = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Lockout.AllowedForNewUsers = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(60);
options.Lockout.MaxFailedAccessAttempts = 4;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
});
services.AddCors();
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<SchoolAuthenticationManager>();
var userId = context.Principal.Identity.Name;
var user = userService.FindByNameAsync(userId);
if (user == null)
{
// return unauthorized if user no longer exists
context.Fail("Unauthorized");
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = (string)Configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Issuer"),
ValidAudience = (string)Configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Audience"),
TokenDecryptionKey = new SymmetricSecurityKey(key),
ClockSkew = TimeSpan.FromMinutes(0),
};
});
services.AddIdentity<SchoolUser, IdentityRole>()
.AddEntityFrameworkStores<ProjectDbContext>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(x => x
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin());
app.UseSwagger();
//{..................}
app.UseAuthentication();
app.UseMvc();
}
}
}
这是我在调试会话中看到的消息
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 66.1768ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: **Request starting HTTP/1.1 GET http://localhost:49873/Account/Login?ReturnUrl=%2Fapi%2Fvalues%2Fget**
Microsoft.EntityFrameworkCore.Infrastructure:Information: Entity Framework Core 2.2.6-servicing-10079 initialized 'ProjectDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: Non
The thread 0x2a2c has exited with code 0 (0x0).
'iisexpress.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.6\System.Xml.ReaderWriter.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (6,238ms) [Parameters=[@__get_Item_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [e].[Id], [e].[AccessFailedCount], [e].[ConcurrencyStamp], [e].[Discriminator], [e].[Email], [e].[EmailConfirmed], [e].[LockoutEnabled], [e].[LockoutEnd], [e].[NormalizedEmail], [e].[NormalizedUserName], [e].[PasswordHash], [e].[PhoneNumber], [e].[PhoneNumberConfirmed], [e].[SecurityStamp], [e].[TwoFactorEnabled], [e].[UserName]
FROM [AspNetUsers] AS [e]
WHERE ([e].[Discriminator] = N'SchoolUser') AND ([e].[Id] = @__get_Item_0)
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (600ms) [Parameters=[@__user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = @__user_Id_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (175ms) [Parameters=[@__userId_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [role].[Name]
FROM [AspNetUserRoles] AS [userRole]
INNER JOIN [AspNetRoles] AS [role] ON [userRole].[RoleId] = [role].[Id]
WHERE [userRole].[UserId] = @__userId_0
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: **Request finished in 11822.4543ms 404**
这是我的身份验证控制器
[Route("{api}/{controller}/{action}")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly ProjectAuthenticationManager _authenticationManager; //Inherited from AspNetCore.Identity
private readonly ProjectSignInManager _signInManager;//Inherited from AspNetCore.Identity
private readonly AppSettings _appSettings;
private readonly IMapper _mapper;
private readonly ILogger<LoginModel> _logger;
private readonly IConfiguration _configuration;
public AuthenticationController(ProjectAuthenticationManager AuthenticationManager,
ProjectSignInManager signInManager,
ILogger<LoginModel> logger,
IOptions<AppSettings> setting,
IConfiguration configuration)
{
_authenticationManager = AuthenticationManager;
_signInManager = signInManager;
_logger = logger;
_appSettings = setting.Value;
_configuration = configuration;
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> AuthenticateAsync([FromBody]LoginModel loginModel)
{
ProjectUser user = await _authenticationManager.FindByNameAsync(loginModel.Username);
var result = await _signInManager.PasswordSignInAsync(loginModel.Username, loginModel.Password, false, lockoutOnFailure: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
Audience = (string)_configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Audience"),
EncryptingCredentials = null,
IssuedAt = DateTime.Now,
Issuer = (string)_configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Issuer"),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
// return basic user info (without password) and token to store client side
return Ok(new
{
Id = user.Id,
Username = user.UserName,
//FirstName = user.FirstName,
//LastName = user.LastName,
Token = tokenString
});
}
}
[HttpPost]
public async Task<IActionResult> Register([FromBody]RegisterModel registerModel)
{
if (ModelState.IsValid)
{
var user = new ProjectUser { UserName = registerModel.Username, Email = registerModel.Email };
var result = await _authenticationManager.CreateAsync(user, registerModel.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
//var code = await _authenticationManager.GenerateEmailConfirmationTokenAsync(user);
//var callbackUrl = Url.Page(
// "/Account/ConfirmEmail",
// pageHandler: null,
// values: new { userId = user.Id, code = code },
// protocol: Request.Scheme);
//await _emailSender.SendEmailAsync(registerModel.Email, "Confirm your email",
// $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
return Created("", "User account created successfully.");
}
}
// If we got this far, something failed.
return BadRequest(new string("Something bad happened"));
}
这是我正在尝试访问的资源
[Route("{api}/{controller}/{action}")]
[ApiController]
[Authorize]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return "value";
}
}
答案 0 :(得分:0)
我已经解决了一段时间了,这次我在另一个项目中创建了相同的身份验证,这一次是分别添加组件。我发现Configure
方法中的这段代码引起了问题。
app.UseEndpointRouting();
app.UseEndpoint();
但这是什么意思?
注释掉后,请求成功通过身份验证,但是当我取消注释时,它会将我重定向到AspNetCore.Identity
的默认身份验证路由http://localhost:49873/Account/Login