【问题标题】:How to make MVC5 AccountController talk to a legacy security dll如何让 MVC5 AccountController 与旧版安全 dll 对话
【发布时间】:2017-03-02 19:36:01
【问题描述】:

我继承了一个遗留框架 dll,它有自己的登录方法和查看用户所属的“组”。这是一个非常典型的本土安全模块。您通过旧的 dll 方法与它交谈。它不使用或与任何现代 .Net 身份验证/授权框架对话。

我创建了一个 MVC5 应用程序并获得了很多样板文件,包括 AccountController,我完全认为最好使用所有这些样板文件,因为那是“按规范进行编码”而不是“违背规范”。

我想对 MVC 项目样板代码进行最小 更改,以使其从旧版 dll 中获得答案。一旦它获得了这些身份验证/授权答案,我希望它继续运行,就好像它从 .Net 框架中获得了这些答案一样。

仅以登录场景为例,

这是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 框架意识以及我是谁等。我完全理解角色是不同的但现在我会满足于只登录并让网站表现得像我使用它的代码那样。

更新

这可能与它在App_StartIdentityConfig 文件中创建的ApplicationUserManagerApplicationSignInManager 有关。

ASP.NET MVC and Identity 2.0: Understanding the Basics

【问题讨论】:

    标签: authentication asp.net-mvc-5


    【解决方案1】:

    我在这里使用了一种新方法来得出答案。我得出答案的方式比答案本身更重要。

    寻找答案的方法

                                            

    1. 查找 .Net Framework 源代码。在这种情况下,它是 Microsoft.AspNet.Identity.CoreMicrosoft.AspNet.Identity.Owin
    2. 找到我想与之交互操作的“黑匣子”类。在这种情况下,它是 UserManagerSignInManager
    3. 找到我想要交互操作的入口“黑盒”方法。在这种情况下,它是 SignInManager.PasswordSignInAsync(),在 AccountController.Login(LoginViewModel model, string returnUrl) 中调用
    4. 从源代码中复制方法并将其粘贴到样板子类中。在这种情况下,它是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()
          //    && (await UserManager.GetValidTwoFactorProvidersAsync(user.Id).WithCurrentCulture()).Count > 0
          //    && !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) 的知识,而不是我在数小时的阅读或谷歌搜索中学到的知识。它可能并不完美,但我可以使用相同的过程来改进我所拥有的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-10
      • 2015-09-06
      • 2010-09-12
      • 1970-01-01
      • 1970-01-01
      • 2018-04-06
      • 2018-02-03
      • 2020-08-15
      相关资源
      最近更新 更多