Add password changing
This commit is contained in:
@ -23,7 +23,7 @@
|
|||||||
<RadzenPanelMenu>
|
<RadzenPanelMenu>
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
<Authorized>
|
<Authorized>
|
||||||
<RadzenPanelMenuItem Text="@context.User.Identity?.Name" />
|
<RadzenPanelMenuItem Path="/account/manage" Text="@context.User.Identity?.Name" />
|
||||||
<RadzenPanelMenuItem Path="/identity/logout" Text="Выйти" Icon="logout" />
|
<RadzenPanelMenuItem Path="/identity/logout" Text="Выйти" Icon="logout" />
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
</RadzenFormField>
|
</RadzenFormField>
|
||||||
<RadzenFormField Text="Пароль" Variant="Variant.Outlined">
|
<RadzenFormField Text="Пароль" Variant="Variant.Outlined">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
<RadzenPassword Name="Password" @bind-Value=@Input.Password AutoCompleteType="AutoCompleteType.NewPassword" />
|
<RadzenPassword Name="Password" @bind-Value=@Input.Password AutoCompleteType="AutoCompleteType.CurrentPassword" />
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
<Helper>
|
<Helper>
|
||||||
<RadzenRequiredValidator Component="Password" Text="Поле 'Пароль' обязательно к заполнению" />
|
<RadzenRequiredValidator Component="Password" Text="Поле 'Пароль' обязательно к заполнению" />
|
||||||
|
|||||||
96
Hcs.WebApp/Components/Pages/Account/Manage.razor
Normal file
96
Hcs.WebApp/Components/Pages/Account/Manage.razor
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
@page "/account/manage"
|
||||||
|
|
||||||
|
@using Hcs.WebApp.Services
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
|
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
@inject IdentityService IdentityService
|
||||||
|
|
||||||
|
<PageTitle>Профиль</PageTitle>
|
||||||
|
|
||||||
|
<RadzenTabs RenderMode="TabRenderMode.Server">
|
||||||
|
<Tabs>
|
||||||
|
<RadzenTabsItem Text="Пароль">
|
||||||
|
<div style="max-width: 420px">
|
||||||
|
<RadzenTemplateForm TItem="PasswordInputModel" Data=@PasswordInput Method="post" Submit="@ChangePassword">
|
||||||
|
<RadzenAlert AlertStyle="AlertStyle.Danger" Variant="Variant.Flat" Shade="Shade.Lighter" Visible="@hasError">
|
||||||
|
@errorMessage
|
||||||
|
</RadzenAlert>
|
||||||
|
<RadzenAlert AlertStyle="AlertStyle.Success" Variant="Variant.Flat" Shade="Shade.Lighter" Visible="@hasSuccess">
|
||||||
|
Пароль успешно изменен
|
||||||
|
</RadzenAlert>
|
||||||
|
<RadzenStack Gap="1rem" class="rz-p-sm-12">
|
||||||
|
<RadzenText TextStyle="TextStyle.H5">Смена пароля</RadzenText>
|
||||||
|
<RadzenFormField Text="Текущий пароль" Variant="Variant.Outlined">
|
||||||
|
<ChildContent>
|
||||||
|
<RadzenPassword Name="OldPassword" @bind-Value=@PasswordInput.OldPassword AutoCompleteType="AutoCompleteType.CurrentPassword" />
|
||||||
|
</ChildContent>
|
||||||
|
<Helper>
|
||||||
|
<RadzenRequiredValidator Component="OldPassword" Text="Поле 'Текущий пароль' обязательно к заполнению" />
|
||||||
|
<RadzenLengthValidator Component="OldPassword" Min="6" Text="Длина поля 'Текущий пароль' должна быть не меньше 6" />
|
||||||
|
<RadzenLengthValidator Component="OldPassword" Max="100" Text="Длина поля 'Текущий пароль' должна быть не больше 100" />
|
||||||
|
</Helper>
|
||||||
|
</RadzenFormField>
|
||||||
|
<RadzenFormField Text="Новый пароль" Variant="Variant.Outlined">
|
||||||
|
<ChildContent>
|
||||||
|
<RadzenPassword Name="NewPassword" @bind-Value=@PasswordInput.NewPassword AutoCompleteType="AutoCompleteType.NewPassword" />
|
||||||
|
</ChildContent>
|
||||||
|
<Helper>
|
||||||
|
<RadzenRequiredValidator Component="NewPassword" Text="Поле 'Новый пароль' обязательно к заполнению" />
|
||||||
|
<RadzenLengthValidator Component="NewPassword" Min="6" Text="Длина поля 'Новый пароль' должна быть не меньше 6" />
|
||||||
|
<RadzenLengthValidator Component="NewPassword" Max="100" Text="Длина поля 'Новый пароль' должна быть не больше 100" />
|
||||||
|
</Helper>
|
||||||
|
</RadzenFormField>
|
||||||
|
<RadzenFormField Text="Повторите новый пароль" Variant="Variant.Outlined">
|
||||||
|
<ChildContent>
|
||||||
|
<RadzenPassword Name="ConfirmNewPassword" @bind-Value=@PasswordInput.ConfirmNewPassword AutoCompleteType="AutoCompleteType.NewPassword" />
|
||||||
|
</ChildContent>
|
||||||
|
<Helper>
|
||||||
|
<RadzenRequiredValidator Component="ConfirmNewPassword" Text="Поле 'Повторите новый пароль' обязательно к заполнению" />
|
||||||
|
<RadzenCompareValidator Value=@PasswordInput.NewPassword Component="ConfirmNewPassword" Text="Пароли должны совпадать" />
|
||||||
|
</Helper>
|
||||||
|
</RadzenFormField>
|
||||||
|
<RadzenButton ButtonType="ButtonType.Submit" Text="Сменить пароль"></RadzenButton>
|
||||||
|
</RadzenStack>
|
||||||
|
</RadzenTemplateForm>
|
||||||
|
</div>
|
||||||
|
</RadzenTabsItem>
|
||||||
|
</Tabs>
|
||||||
|
</RadzenTabs>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
sealed class PasswordInputModel
|
||||||
|
{
|
||||||
|
public string OldPassword { get; set; } = "";
|
||||||
|
|
||||||
|
public string NewPassword { get; set; } = "";
|
||||||
|
|
||||||
|
public string ConfirmNewPassword { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasError;
|
||||||
|
string? errorMessage;
|
||||||
|
bool hasSuccess;
|
||||||
|
|
||||||
|
[SupplyParameterFromForm]
|
||||||
|
PasswordInputModel PasswordInput { get; set; } = new();
|
||||||
|
|
||||||
|
async Task ChangePassword()
|
||||||
|
{
|
||||||
|
hasError = false;
|
||||||
|
hasSuccess = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await IdentityService.ChangePassword(PasswordInput.OldPassword, PasswordInput.NewPassword);
|
||||||
|
|
||||||
|
hasSuccess = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
hasError = true;
|
||||||
|
errorMessage = e.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,7 +33,7 @@
|
|||||||
<RadzenPassword Name="ConfirmPassword" @bind-Value=@Input.ConfirmPassword AutoCompleteType="AutoCompleteType.NewPassword" />
|
<RadzenPassword Name="ConfirmPassword" @bind-Value=@Input.ConfirmPassword AutoCompleteType="AutoCompleteType.NewPassword" />
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
<Helper>
|
<Helper>
|
||||||
<RadzenRequiredValidator Component="ConfirmPassword" Text="Поле 'Пароль' обязательно к заполнению" />
|
<RadzenRequiredValidator Component="ConfirmPassword" Text="Поле 'Повторите пароль' обязательно к заполнению" />
|
||||||
<RadzenCompareValidator Value=@Input.Password Component="ConfirmPassword" Text="Пароли должны совпадать" />
|
<RadzenCompareValidator Value=@Input.Password Component="ConfirmPassword" Text="Пароли должны совпадать" />
|
||||||
</Helper>
|
</Helper>
|
||||||
</RadzenFormField>
|
</RadzenFormField>
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
using Hcs.WebApp.Data;
|
using Hcs.WebApp.Data;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Radzen;
|
using Radzen;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace Hcs.WebApp.Controllers
|
namespace Hcs.WebApp.Controllers
|
||||||
{
|
{
|
||||||
[Route("identity/[action]")]
|
[Route("identity/")]
|
||||||
|
[Authorize]
|
||||||
public class IdentityController(
|
public class IdentityController(
|
||||||
IUserStore<AppUser> userStore,
|
IUserStore<AppUser> userStore,
|
||||||
UserManager<AppUser> userManager,
|
UserManager<AppUser> userManager,
|
||||||
@ -16,6 +19,8 @@ namespace Hcs.WebApp.Controllers
|
|||||||
private readonly SignInManager<AppUser> signInManager = signInManager;
|
private readonly SignInManager<AppUser> signInManager = signInManager;
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[Route("register")]
|
||||||
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> Register(string userName, string password, string returnUrl)
|
public async Task<IActionResult> Register(string userName, string password, string returnUrl)
|
||||||
{
|
{
|
||||||
var user = Activator.CreateInstance<AppUser>();
|
var user = Activator.CreateInstance<AppUser>();
|
||||||
@ -24,7 +29,7 @@ namespace Hcs.WebApp.Controllers
|
|||||||
var result = await userManager.CreateAsync(user, password);
|
var result = await userManager.CreateAsync(user, password);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
var error = string.Join(", ", result.Errors.Select(error => error.Description));
|
var error = string.Join(", ", result.Errors.Select(x => x.Description));
|
||||||
if (!string.IsNullOrEmpty(returnUrl))
|
if (!string.IsNullOrEmpty(returnUrl))
|
||||||
{
|
{
|
||||||
return Redirect($"/account/register?error={error}&returnUrl={Uri.EscapeDataString(returnUrl)}");
|
return Redirect($"/account/register?error={error}&returnUrl={Uri.EscapeDataString(returnUrl)}");
|
||||||
@ -46,6 +51,8 @@ namespace Hcs.WebApp.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[Route("login")]
|
||||||
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> Login(string userName, string password, string returnUrl)
|
public async Task<IActionResult> Login(string userName, string password, string returnUrl)
|
||||||
{
|
{
|
||||||
var result = await signInManager.PasswordSignInAsync(userName, password, false, false);
|
var result = await signInManager.PasswordSignInAsync(userName, password, false, false);
|
||||||
@ -69,11 +76,29 @@ namespace Hcs.WebApp.Controllers
|
|||||||
return Redirect(returnUrl);
|
return Redirect(returnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("logout")]
|
||||||
public async Task<IActionResult> Logout()
|
public async Task<IActionResult> Logout()
|
||||||
{
|
{
|
||||||
await signInManager.SignOutAsync();
|
await signInManager.SignOutAsync();
|
||||||
|
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("change-password")]
|
||||||
|
public async Task<IActionResult> ChangePassword(string oldPassword, string newPassword)
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
var result = await userManager.ChangePasswordAsync(user, oldPassword, newPassword);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = string.Join(", ", result.Errors.Select(x => x.Description));
|
||||||
|
return BadRequest(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,13 @@ builder.Services
|
|||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddRadzenComponents();
|
builder.Services.AddRadzenComponents();
|
||||||
|
|
||||||
builder.Services.AddHttpClient("Hcs.WebApp").AddHeaderPropagation(x => x.Headers.Add("Cookie"));
|
builder.Services.AddHttpClient("WithIdentity").AddHeaderPropagation(x => x.Headers.Add("Cookie"));
|
||||||
builder.Services.AddHeaderPropagation(x => x.Headers.Add("Cookie"));
|
builder.Services.AddHeaderPropagation(x => x.Headers.Add("Cookie"));
|
||||||
builder.Services.AddAuthentication();
|
builder.Services.AddAuthentication();
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<IdentityService>();
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("IdentityConnection") ?? throw new InvalidOperationException("<22><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> 'IdentityConnection'");
|
var connectionString = builder.Configuration.GetConnectionString("IdentityConnection") ?? throw new InvalidOperationException("<22><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> 'IdentityConnection'");
|
||||||
builder.Services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(connectionString));
|
builder.Services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(connectionString));
|
||||||
|
|
||||||
|
|||||||
25
Hcs.WebApp/Services/IdentityService.cs
Normal file
25
Hcs.WebApp/Services/IdentityService.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Hcs.WebApp.Services
|
||||||
|
{
|
||||||
|
public class IdentityService(NavigationManager navigationManager, IHttpClientFactory factory)
|
||||||
|
{
|
||||||
|
private readonly Uri baseUri = new($"{navigationManager.BaseUri}identity/");
|
||||||
|
private readonly HttpClient httpClient = factory.CreateClient("WithIdentity");
|
||||||
|
|
||||||
|
public async Task ChangePassword(string oldPassword, string newPassword)
|
||||||
|
{
|
||||||
|
var uri = new Uri($"{baseUri}change-password");
|
||||||
|
var content = new FormUrlEncodedContent(new Dictionary<string, string> {
|
||||||
|
{ "oldPassword", oldPassword },
|
||||||
|
{ "newPassword", newPassword }
|
||||||
|
});
|
||||||
|
var response = await httpClient.PostAsync(uri, content);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var message = await response.Content.ReadAsStringAsync();
|
||||||
|
throw new ApplicationException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user