Могу ли я использовать удостоверение asp.net для отправки электронного письма для сброса пароля БЕЗ использования сущностной инфраструктуры для управления моими пользователями? - PullRequest
0 голосов
/ 09 февраля 2019

У меня есть приложение 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»

, так что ... мне интересно,это просто невозможно, или что мне нужно сделать, чтобы получить эти токены?

...