У меня есть приложение asp.net MVC, которое частично использует удостоверение asp.net для управления пользователями.Я говорю частично, потому что единственная часть идентичности, которую мы на самом деле используем, - это часть, которая генерирует требование для файла cookie пользователей, чтобы показать, что они вошли в систему. Мы не используем те части инфраструктуры удостоверений, которые требуют, чтобы структура сущностей выполняла сохранение иобновление профилей пользователей, все, что делается вручную.
, поэтому задача, которую я сейчас имею, состоит в том, чтобы переписать способ, которым мы позволяем пользователям сбрасывать свои пароли.Мы хотим отправить пользователю ссылку, по которой он щелкает, с кодом, который он затем проверяет, что позволяет ему установить новый пароль.
Я подумал о том, чтобы использовать для этого свою собственную криптографию, но в целом это выглядит так:плохая идея, я не специалист по криптографии.Таким образом, использование встроенных функций, предоставляемых удостоверением asp.net, кажется хорошей идеей, но у меня серьезные проблемы, расстраивающие EF от удостоверения asp.net.Давайте посмотрим на некоторый код, который у меня уже есть, и на то, что я хочу сделать.
Мой вопрос: возможно ли, чтобы удостоверение asp.net генерировало токены и коды для сброса пароля, даже если я решу не делать этого?использовать Entity Framework?
некоторые используемые мной пакеты nuget:
Microsoft.AspNet.Identity.Core v2.2.1
Microsoft.AspNet.Identity.EntityFramework v2.2.1 <-- not actually using it, just a dependency that it must be included
Microsoft.Owin" v3.0.1
Microsoft.Owin.Host.SystemWeb v3.0.1
Microsoft.Owin.Security" v3.0.1
Microsoft.Owin.Security.Cookies v3.0.1
Microsoft.Owin.Security.OAuth" v3.0.1
Owin v1.0
~ \ App_Start \ AuthConfig.cs
using System;
using System.Configuration;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
namespace Arcteryx.Web
{
public static class AuthConfig
{
public static void Config(IAppBuilder app)
{
CookieAuthenticationOptions cookieAuthenticationOptions = new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/account/login"),
CookieSecure = CookieSecureOption.Never,
CookieName = ConfigurationManager.AppSettings["AuthenticationCookieName"] ?? "MyAppAuthentication",
ExpireTimeSpan = new TimeSpan(0, 24, 0, 0)
};
app.UseCookieAuthentication(cookieAuthenticationOptions);
}
}
}
~ \ Controllers \ Account \ AccountController.cs
// POST: /Account/LoginPost
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[Route("{country}/{language}/account/login")]
public virtual async Task<ActionResult> LoginPost(LoginViewModel model, string ReturnUrl)
{
if (ModelState.IsValid)
{
var account = new AccountLoginModel();
Mapper.Map(model, account);
account = _account.Login(account);
if (account.LoginSuccess)
{
if (string.IsNullOrEmpty(ReturnUrl))
return RedirectToAction("ViewProfile");
return Redirect($"~/{ReturnUrl}");
}
model.LoginErrorMessage = account.Message;
}
var loginViewModel = await LoginViewModel(ReturnUrl);
Mapper.Map(model, loginViewModel);
return View("~/Views/Account/Login.cshtml", loginViewModel);
}
_account в приведенном выше файле является внедренным DI экземпляром логики моей учетной записи, некоторые соответствующие части этого файла находятся здесь: ~ \ Account \ AccountLogic.cs
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using Claim = System.Security.Claims.Claim;
public class AccountLogic : IAccountLogic
{
private readonly ISettingsProvider _settingsProvider;
private readonly ICookieManager _cookieManager;
private readonly IAuthenticationManager _authenticationManager;
private readonly IAuthenticationService _authenticationService;
private readonly IUserService _userService;
private readonly ILogging _logging;
public AccountLogic(ISettingsProvider settingsProvider, ICookieManager cookieManager, IAuthenticationService authenticationService, IAuthenticationManager authenticationManager, IUserService userService, ILogging logging)
{
_settingsProvider = settingsProvider;
_cookieManager = cookieManager;
_authenticationService = authenticationService;
_authenticationManager = authenticationManager;
_userService = userService;
_logging = logging;
_credential = new NetworkCredential(_settingsProvider.SettingsModel.xx, EncryptDecrypt.Decrypt(settingsProvider.SettingsModel.yy), settingsProvider.SettingsModel.zz);
}
public AccountLoginModel Login(AccountLoginModel account)
{
account = _authenticationService.Login(account, _credential);
if (account.LoginSuccess)
{
IdentitySignin(account);
else
{ //we failed to login. omitted error logging code here
}
return account;
}
public void UpdateClaim(AccountBaseModel account)
{ //this updates the users claim with everything we need to know about him
//you could do the same by calling login, but then you would need his password
IdentitySignin(account);
}
public bool Logout()
{
try
{
var user = _authenticationManager.User;
if (user != null && user.Identity.IsAuthenticated)
IdentitySignout();
// Remove the login cookies
_cookieManager.DeleteCookie("IsLogged");
}
catch (Exception)
{
//todo log some error here if we can't sign out.
return false;
}
return true;
}
private void IdentitySignin(AccountBaseModel account)
{
// create claims
var claims = new List<Claim>
{
new Claim("Email", account.Email),
new Claim("CustomerNumber", account.CustomerNumber.ToString()),
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
// sign the user in, basically this gives user cookies which cause him to be seen as authenticated
_authenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = false,
ExpiresUtc = DateTime.UtcNow.AddDays(1)
}, identity);
}
~ AuthenticationService.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Net;
using AutoMapper;
public class MyAuthenticationService : IAuthenticationService
{
public AccountLoginModel Login(AccountLoginModel account, NetworkCredential credential)
{
if (account == null || account.Email == null || account.Password == null)
throw new ArgumentNullException("null arguments");
if (string.IsNullOrEmpty(credential.Domain) || credential.UserName == null || credential.Password == null)
throw new NullReferenceException("Null web-service URL, username, password, and/or domain.");
account.Message = "";
bool returnValue;
string WebIdent = string.Empty;
//we need to send a DT in, gets us our user details.
DataTable loginDt = null;
using (var myservice = myfactory.Create(credential))
{
try
{
returnValue = myservice .CustomerLogin(ref loginDt, account.Email, account.Password, ref WebIdent,
false);
}
catch (Exception)
{
returnValue = false;
//log some error
account.Reason = AccountBaseModel.AccountFailureReason.CANTCONNECT;
}
if (returnValue)
{
//omitted some code here, but basically, at this point, we have successfully logged in, by sending our username and password elsewhere to be validated.
}
return account;
}
ОК, отлично, все работает.как только пользователь входит в систему и мы успешно проверяем его имя пользователя и пароль, мы даем ему cookie-файл с претензиями, а затем мы проверяем, вошел ли он с помощью этого cookie-файла, используя атрибут [authorize] в наших действиях контроллера.
OK,Теперь, что, если бы я хотел как-то: - получить запрос от пользователя, который забыл свой пароль, он знает только адрес электронной почты, с которым он подписан.-примите отправленное им электронное письмо, сгенерируйте, криптографически защищенную ссылку и 8-значный код, и отправьте его по электронной почте пользователю (мне действительно нужны коды, часть отправки по электронной почте, которую я знаю, как это сделать), - выполните другое действие mvc, котороепользователь попадает по адресу, скажем / resetlink? id = CgT461JkKczIOQUvHK6o1KDs - на этой странице найдите ссылку в базе данных и убедитесь, что она у нас есть.спросите у пользователя его код, посмотрите, совпадают ли они.отправить пользователя на новую страницу с двумя текстовыми полями для ввода нового пароля и подтверждения нового пароля.-сохраните новый пароль в базе данных.
Я думаю, что поток достаточно четко определен, и мне ясно, как это сделать, если я напишу свои собственные случайно сгенерированные строки ссылок, которые достаточно сложно угадать.Также нет проблем с генерацией случайных 8-значных кодов.
хотя я беспокоюсь, что я что-то упущу в реализации и что это можно будет взломать.Я рассматривал asp.net как решение END TO END, но сейчас это не вариант для меня (у меня 6 сайтов, работающих на одной базе пользователей, которая находится в другой, удаленной базе данных, с которой я могу разговаривать только с использованием мылавеб-сервис).поэтому я не могу хранить пользователей с моими веб-сайтами, они должны оставаться в одном месте, и я вообще не могу использовать Entity Framework.
Код, который я хотел бы использовать, похож наэто: https://docs.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
// If we got this far, something failed, redisplay form
return View(model);
}
в частности, эта строка: string code = await UserManager.GeneratePasswordResetTokenAsync (user.Id);
однако .. У меня нет usermanager.
https://docs.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
Как получить класс usermanger?Что ж, я просто даю ему свой контекст EF db ... но у меня нет ни того, ни другого, ни базы данных, которую я могу читать или записывать, чтобы хранить пользователей ... Теоретически это будет сделано примерно так: (в authconfig.cs)
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
^^ но да, у меня нет приложения dbcontext, которое является зависимостью EF.кроме того, не уверен, что я делаю неправильно, но в моем «приложении» нет такого метода, я получаю следующее: «IappBuilder не содержит определения для CreatePerOwinContext»
, так что ... мне интересно,это просто невозможно, или что мне нужно сделать, чтобы получить эти токены?