Как заставить MVC5 AccountController взаимодействовать с устаревшей библиотекой безопасности

#authentication #asp.net-mvc-5

#аутентификация #asp.net-mvc-5

Вопрос:

Я унаследовал устаревшую библиотеку dll framework с ее собственными методами для входа в систему и просмотра того, к каким «группам» принадлежит пользователь. Это довольно типичный домашний модуль безопасности. Вы общаетесь с ней с помощью устаревших методов dll. Она не использует и не взаимодействует ни с одной из современных.Сетевая платформа аутентификации / авторизации.

Я создал приложение MVC5 и получил множество шаблонов, включая AccountController и я полностью считаю, что лучше всего использовать все это, потому что это «кодирование с учетом», а не «против зерна».

Я хочу внести минимальные изменения в шаблонный код проекта MVC, чтобы он получал ответы из устаревшей библиотеки dll. Как только у него появятся эти ответы на аутентификацию / авторизацию, я хочу, чтобы он продолжал, как если бы он получил эти ответы от .Net framework.

Использовать только сценарий входа в систему в качестве примера,

Вот данный шаблонный метод внутри AccountController :

 [HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
  if (!ModelState.IsValid)
  {
    return View(model);
  }

  //SignInStatus result = Login(model.Email, model.Password);

  // This doesn't count login failures towards account lockout
  // To enable password failures to trigger account lockout, change to shouldLockout: true
  var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
  switch (result)
  {
    case SignInStatus.Success:
      return RedirectToLocal(returnUrl);
    case SignInStatus.LockedOut:
      return View("Lockout");
    case SignInStatus.RequiresVerification:
      return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
    case SignInStatus.Failure:
    default:
      ModelState.AddModelError("", "Invalid login attempt.");
      return View(model);
  }
}
  

и вот моя процедура получения ответа «достаточно похожего для сопоставления» из моей устаревшей библиотеки dll:

 private SignInStatus Login(string userId, string password)
{
  string SoftwareLicenses = "blahblahblah";
  try
  {
    UserCredentials = UserProfileType.Login(userId, password, 0, SoftwareLicenses);
    return SignInStatus.Success;
  }
  catch (AtlasBusinessRuleException abrex)
  {
    switch (abrex.Message)
    {
      case "Invalid Login Name entered":
        return SignInStatus.Failure;
      case "Invalid Password entered":
        return SignInStatus.Failure;
      case "Your password has expired. Please change the password.":
        return SignInStatus.LockedOut;
      default:
        return SignInStatus.Failure;
    }

  }
}
  

Видите ли, если я просто раскомментирую свой вызов моей подпрограммы в стандартной процедуре, этого недостаточно, чтобы включить все .Осведомленность Net Framework о том, что я вошел в систему и кто я такой и т.д. Я полностью понимаю, что роли разные, но прямо сейчас я соглашусь на то, чтобы просто войти в систему и заставить веб-сайт действовать так, как будто я использовал для этого его код.

Обновить

Вероятно, это как-то связано с ApplicationUserManager и ApplicationSignInManager тем, что он создает в IdentityConfig файле в App_Start .

ASP.NET MVC и идентификация 2.0: понимание основ

Ответ №1:

Я использовал новую методологию, чтобы получить ответ здесь. Способ, которым я пришел к ответу, более важен, чем сам ответ.

Методология поиска ответа

                                          введите описание изображения здесь

  1. Найдите исходный код .Net Framework. В этом случае это было Microsoft.AspNet.Identity.Core и Microsoft.AspNet.Identity.Owin
  2. Найдите классы, являющиеся «черным ящиком», с которым я хочу взаимодействовать. В этом случае это было UserManager и SignInManager
  3. Найдите метод ввода «черного ящика», с которым я хочу взаимодействовать. В данном случае это был SignInManager.PasswordSignInAsync() который вызывается в AccountController.Login(LoginViewModel model, string returnUrl)
  4. Скопируйте метод из исходного кода и вставьте его в подкласс boilerplate. В данном случае это было ApplicationSignInManager . Превратите метод в переопределяемый, изменив virtual ключевое слово на override .
  5. Найдите и закомментируйте любой код, который несовместим с моей внутренней библиотекой dll, или просто не будет работать, или не применим к моему приложению. Не слишком комментируйте и ничего не предполагайте. Комментарий консервативен.
  6. Если требуется функциональность, которая может быть предоставлена моей внутренней библиотекой dll, предоставьте ее с помощью моей внутренней библиотеки dll.
  7. Если этот «метод черного ящика» (который мы только что сделали белым ящиком) вызывает еще какие-либо темные методы, повторите шаги 4-6 для этого метода, и так далее, рекурсивно. Вы можете сделать это с отладкой или без нее. Я использовал отладку, потому что это было проще, чем доверять своим глазам, чтобы следить за ходом кода.

Результат использования этой методологии

ApplicationSignInManager.cs (переопределил 2 метода, закомментировал код, который мог бы сломаться)

 using System.Threading.Tasks;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using AACOMvc.Models;
using System.Data.Entity.Utilities;
using Microsoft.AspNet.Identity;
using System;

namespace AAAMvc
{
  // Configure the application sign-in manager which is used in this application.
  public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
  {
    public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
        : base(userManager, authenticationManager)
    {
    }

    public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
    {
      if (UserManager == null)
      {
        return SignInStatus.Failure;
      }
      var user = await UserManager.FindByNameAsync(userName).WithCurrentCulture();
      if (user == null)
      {
        return SignInStatus.Failure;
      }
      //if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
      //{
      //  return SignInStatus.LockedOut;
      //}
      if (await UserManager.CheckPasswordAsync(user, password).WithCurrentCulture())
      {
        //await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();
        return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();
      }
      //if (shouldLockout)
      //{
      //  // If lockout is requested, increment access failed count which might lock out the user
      //  await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();
      //  if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())
      //  {
      //    return SignInStatus.LockedOut;
      //  }
      //}
      return SignInStatus.Failure;
    }



    private async Task<SignInStatus> SignInOrTwoFactor(ApplicationUser user, bool isPersistent)
    {
      var id = Convert.ToString(user.Id);
      //if (await UserManager.GetTwoFactorEnabledAsync(user.Id).WithCurrentCulture()
      //    amp;amp; (await UserManager.GetValidTwoFactorProvidersAsync(user.Id).WithCurrentCulture()).Count > 0
      //    amp;amp; !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id).WithCurrentCulture())
      //{
      //  var identity = new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie);
      //  identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
      //  AuthenticationManager.SignIn(identity);
      //  return SignInStatus.RequiresVerification;
      //}
      await SignInAsync(user, isPersistent, false).WithCurrentCulture();
      return SignInStatus.Success;
    }

    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
      return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
    }


  }
}
  

ApplicationUserManager.cs (переопределил 4 метода, прокомментировал код, который мог бы сломаться, и добавил свой код, чтобы заставить его работать так, как работает устаревшая библиотека dll)

 using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using AACOMvc.Models;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using HerculesBusinessModel;
using System.Security.Claims;
using System.Data.Entity.Utilities;

namespace AAAMvc
{
  // Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
  public class ApplicationUserManager : UserManager<ApplicationUser>
  {
    public ApplicationUserManager(IUserStore<ApplicationUser> store)
        : base(store)
    {
    }

    public static UserCredentialsType UserCredentials
    {
      get
      {
        return (UserCredentialsType)System.Web.HttpContext.Current.Session["UserProfile"];
      }
      set
      {
        System.Web.HttpContext.Current.Session["UserProfile"] = value;
      }
    }

    public override async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
    {
      if (user == null)
      {
        return false;
      }
      await Task.Delay(0);
      string SoftwareLicenses = "blahblahblah";
      try
      {
        UserCredentials = UserProfileType.Login(user.UserName, password, 0, SoftwareLicenses);
        return true;
      }
      catch (HerculesBusinessRuleException ex)
      {
      }
      return false;
    }

    public override async Task<ClaimsIdentity> CreateIdentityAsync(ApplicationUser user, string authenticationType)
    {
      //ThrowIfDisposed();
      if (user == null)
      {
        throw new ArgumentNullException("user");
      }
      const string IdentityProviderClaimType =
          "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
      const string DefaultIdentityProviderClaimValue = "ASP.NET Identity";

      var id = new ClaimsIdentity(authenticationType, ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
      id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id, ClaimValueTypes.String));
      id.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName, ClaimValueTypes.String));
      id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
      if (SupportsUserRole)
      {
        IList<string> roles = await GetRolesAsync(user.Id).WithCurrentCulture();
        foreach (string roleName in roles)
        {
          id.AddClaim(new Claim(ClaimsIdentity.DefaultRoleClaimType, roleName, ClaimValueTypes.String));
        }
      }
      if (SupportsUserClaim)
      {
        //id.AddClaims(await GetClaimsAsync(user.Id).WithCurrentCulture());
      }
      return id;
    }

    ApplicationUser user;
    public override async Task<ApplicationUser> FindByNameAsync(string userName)
    {
      await Task.Delay(0);
      var profile = UserProfileType.FetchUserProfileForLogin(userName);
      if (profile != null)
      {
        user = new ApplicationUser()
        {
          UserName = profile.LoginName,
          Id = profile.Id.ToString()
        };
        foreach (decimal groupId in profile.UserGroups)
        {
          user.Roles.Add(new IdentityUserRole() { UserId = profile.Id.ToString(), RoleId = groupId.ToString() });
        }
      }
      return user;
    }

    public override async Task<IList<string>> GetRolesAsync(string userId)
    {
      ////ThrowIfDisposed();
      //var userRoleStore = GetUserRoleStore();
      //var user = await FindByIdAsync(userId).WithCurrentCulture();
      //if (user == null)
      //{
      //  throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,
      //      userId));
      //}
      //return await userRoleStore.GetRolesAsync(user).WithCurrentCulture();
      await Task.Delay(0);
      return user.Roles.Select(r => r.RoleId).ToList();
    }
  }
}
  

Заключение

Я был удивлен и очень доволен, что несколько роботизированный процесс может дать ответ. Но я был еще более доволен тем, что использование этого процесса научило меня большему о странном новом пространстве имен ( Microsoft.AspNet.Identity ), чем я мог бы узнать за часы чтения или поиска в Google. Возможно, это не идеально, но я могу использовать тот же процесс, чтобы улучшить то, что у меня есть здесь.