我正在使用 ASP.NET Core Identity 在使用Dapper ORM的自定義資料訪問層之上構建一個 ASP.NET Core 5 Web API 。從根本上說,事情按預期作業,但我意識到身份框架提供的密碼驗證規則沒有任何影響,我無法理解發生了什么。這是我所擁有的:
首先,因為我依賴于自定義資料訪問層,所以我提供了 IdentityIUserStore介面的自定義實作。
public class UserStore : IUserStore<AppUser>,
IUserPasswordStore<AppUser>,
IUserEmailStore<AppUser>
{
private IRepository<AppUser> _repository;
public UserStore(IConfiguration configuration)
{
_repository = new AppUserRepository(configuration.GetConnectionString("MyConnectionString"));
}
// IUserStore implementation
// IUserPasswordStore implementation
// IUserEmailStore implementation
}
接下來,有一個系結模型,用于提交創建新帳戶所需的資訊。
public class RegisterBindingModel
{
[Required]
[Display(Name = "UserName")]
public string UserName
{
get;
set;
}
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password
{
get;
set;
}
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare(nameof(Password), ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword
{
get;
set;
}
// remaining required properties
}
接下來,通過以下方式創建新帳戶AccountController:
[Authorize]
[ApiController]
[Route("api/Accounts")]
public class AccountController
{
private readonly UserManager<AppUser> _userManager;
private readonly IPasswordHasher<AppUser> _passwordHasher;
public AccountController(UserManager<AppUser> userManager, IPasswordHasher<AppUser> passwordHasher)
{
_userManager = userManager;
_passwordHasher = passwordHasher;
}
[AllowAnonymous]
[HttpPost]
[Route("Register")]
public async Task<ActionResult> Register([FromBody]RegisterBindingModel model)
{
if(model == null)
{
return BadRequest();
}
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new AppUser()
{
UserName = model.UserName,
Firstname = model.Firstname,
Lastname = model.Lastname,
Email = model.Email,
Gender = model.Gender
};
user.PasswordHash = _passwordHasher.HashPassword(user, model.Password);
IdentityResult result = await _userManager.CreateAsync(user);
return GetHttpResponse(result);
}
[AllowAnonymous]
[HttpPost]
[Route("Token")]
public async Task<IActionResult> Login([FromForm]LoginBindingModel model)
{
if(model == null)
{
return BadRequest();
}
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
AppUser user = await _userManager.FindByNameAsync(model.UserName);
if(user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
{
return Unauthorized();
}
DateTime currentTime = DateTime.UtcNow;
JwtSecurityTokenHandler jwtTokenHandler = new();
SecurityTokenDescriptor tokenDescriptor = new()
{
Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, user.AppUserId.ToString()) }),
IssuedAt = currentTime,
Expires = currentTime.AddHours(_accessTokenValidityInHours),
SigningCredentials = _signingCredentialsProvider.GetSigningCredentials()
};
return Ok(jwtTokenHandler.WriteToken(jwtTokenHandler.CreateToken(tokenDescriptor)));
}
...
}
最后,事情按如下方式連接在一起:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration
{
get;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityCore<AppUser>(options => Configuration.GetSection(nameof(IdentityOptions)).Bind(options));
services.AddScoped<IPasswordHasher<AppUser>, Identity.PasswordHasher<AppUser>>();
services.AddTransient<IUserStore<AppUser>, UserStore>();
...
}
}
相應的設定存盤在appsettings.json檔案中:
{
"IdentityOptions": {
"Password": {
"RequiredLength": 6,
"RequiredUniqueChars": 6,
"RequireNonAlphanumeric": true,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireDigit": true
},
"Lockout": {
"AllowedForNewUsers": true,
"MaxFailedAccessAttempts ": 5,
"DefaultLockoutTimeSpan ": "00:05:00"
}
},
...
}
If I send an HTTP POST request with the necessary account data it literally doesn't matter what the password is. The call succeeds even if I just put 1 as a password which clearly violates the password rules. The statement if(!ModelState.IsValid) happily tells me that everything is fine with the model.
From what I see, ASP.NET Core Identity provides a PasswordValidator that apparently is supposed to validate the password according to the provided settings. That validator does not run in my setup, judging from the results that I get.
It is unclear to me whether things should just work they way they are or whether I need to implement something that I'm unaware of. Does anyone have more insight and can tell me what I'm missing here?
Edit:
I just realized that the default UserManager exposes a list of IPasswordValidator objects. Is the idea that I use that list to validate the password in my Register method of the AccountController?
uj5u.com熱心網友回復:
您if(!ModelState.IsValid)沒有看到任何錯誤的原因是因為Password您的模型RegisterBindingModel中的引數不包含與您的 appsettings.json 上的選項相同的驗證,您只需驗證它是必需的(所以一個字符就可以了)。
如果您想進行相同的驗證,則需要在Password引數上添加更多屬性。我建議你看看這個https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-5.0
uj5u.com熱心網友回復:
我最終修改了該Register方法,如下所示:
[AllowAnonymous]
[HttpPost]
[Route("Register")]
public async Task<ActionResult> Register([FromBody]RegisterBindingModel model)
{
if(model == null)
{
return BadRequest();
}
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
AppUser user = new()
{
UserName = model.UserName,
Firstname = model.Firstname,
Lastname = model.Lastname,
Email = model.Email,
Gender = model.Gender
};
IdentityResult result;
foreach(IPasswordValidator<AppUser> passwordValidator in _userManager.PasswordValidators)
{
result = await passwordValidator.ValidateAsync(_userManager, user, model.Password);
if(!result.Succeeded)
{
return BadRequest(result.Errors);
}
}
user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, model.Password);
result = await _userManager.CreateAsync(user);
return GetHttpResponse(result);
}
默認UserManager包含PasswordValidators允許我訪問所有PasswordValidators. 我只是遍歷它們并呼叫ValidateAsync用戶提交的密碼的方法。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/343448.html
標籤:c# .net asp.net-core asp.net-identity
上一篇:如何在.NETCore中發送和接收包含檔案的多部分/表單資料?
下一篇:如何使用郵遞員發出POST請求?
