简体   繁体   English

MVC获取/模拟存储库中的Windows用户

[英]MVC Get/Impersonate Windows User In Repository

I have an intranet application that uses the Windows username and passes that to a procedure to return data. 我有一个使用Windows用户名并将其传递给过程以返回数据的Intranet应用程序。

  1. I'm using dependency injection, but I don't believe I have the method to get the username separated properly. 我正在使用依赖项注入,但是我不相信有办法正确分离用户名。
  2. I'm trying to keep this secure by not passing in the username as a parameter, but I also want to be able to impersonate (or bypass my GetWindowsUser() method) and send in another username so I can test results for other users. 我试图通过不传递用户名作为参数来确保这一点的安全,但是我也希望能够模拟(或绕过我的GetWindowsUser()方法)并发送另一个用户名,以便为其他用户测试结果。
    • One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository. 我的一个想法是在另一个页面中使用另一个(模拟的)用户名设置会话变量,然后在获取实际用户名之前先检查该会话变量是否存在,但是我不知道如何访问会话变量在存储库中。

WEB API CONTROLLER Web API控制器

public class DropDownDataController : ApiController
{
    private IDropDownDataRepository _dropDownDataRepository;        

    //Dependency Injection using Unity.WebAPI NuGet Package
    public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
    {
        _dropDownDataRepository = dropDownDataRepository;
    }

    [HttpGet]
    public HttpResponseMessage MyList()
    {
        try
        {
            return _dropDownDataRepository.MyList();
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }
}

REPOSITORY 仓库

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    private DatabaseEntities db = new DatabaseEntities();

    public HttpResponseMessage MyList()
    {
        //(This should be separated somehow, right?) 
        //Create a new instance of the Utility class
        Utility utility = new Utility();
        //Grab the windowsUser from the method
        var windowsUser = utility.GetWindowsUser();

        //Pass windowsUser parameter to the procedure
        var sourceQuery = (from p in db.myProcedure(windowsUser)
                           select p).ToList();

        string result = JsonConvert.SerializeObject(sourceQuery);
        var response = new HttpResponseMessage();
        response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");

        return response;            
    }
}

INTERFACE 接口

public interface IDropDownDataRepository : IDisposable
{
    HttpResponseMessage MyList();        
}

UTILITY CLASS 实用程序类

public class Utility
{
    public string GetWindowsUser()
    {
        //Get the current windows user
        string windowsUser = HttpContext.Current.User.Identity.Name;        

        return windowsUser;
    }
}

UPDATE 1 更新1

In addition to what Nikolai and Brendt posted below, the following is also needed to allow Web Api controllers work with the session state. 除了下面的Nikolai和Brendt发表的内容之外,还需要以下内容来允许Web Api控制器使用会话状态。 Accessing Session Using ASP.NET Web API 使用ASP.NET Web API访问会话

Abstract the Utility class and inject it into the repository. 提取Utility类并将其注入到存储库中。 Then you can stub or mock for testing. 然后,您可以存根或模拟进行测试。

public interface IUtility
{
    string GetWindowsUser();
}

public class TestUtility : IUtility
{
    public string GetWindowsUser()
    {
        return "TestUser";
    }
}

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{

    private IUtility _utility;

    public DropDownDataRepository(IUtility utility)
    {
        _utility = utility;
    }

}

EDIT 编辑

Also the repository should not return an HTTPResponseMessage type it should just return a List<T> of the domain model you're accessing. 此外,存储库不应返回HTTPResponseMessage类型,而应仅返回您正在访问的域模型的List<T>

ie

public List<Model> MyList()
{
    //Grab the windowsUser from the method
    var windowsUser = _utility.GetWindowsUser();

    //Pass windowsUser parameter to the procedure
    var sourceQuery = (from p in db.myProcedure(windowsUser)
                       select p).ToList();

    return sourceQuery           
}

Then move the JSON portion to the controller. 然后将JSON部分移至控制器。

One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository. 我的一个想法是在另一个页面中使用另一个(模拟的)用户名设置会话变量,然后在获取实际用户名之前先检查该会话变量是否存在,但是我不知道如何访问会话变量在存储库中。

Potentially, if you add in a dependency to session, you need to isolate it, eg 潜在地,如果您向会话添加依赖项,则需要隔离它,例如

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    // ... other fields

    private ISession session;

    public DropDownDataRepository(ISession session)
    {
        this.session = session;
    }

    public HttpResponseMessage MyList()
    {
         var myUserName = this.session.UserName;
         // ... etc

With ISession being something like: 与ISession类似:

 public interface ISession
 {
      string UserName { get; }
 }

Implemented as: 实施为:

 public class MySession : ISession
 {
     public string UserName
     {
         get
         {
            // potentially do some validation and return a sensible default if not present in session
            return HttpContext.Current.Session["UserName"].ToString();
         }
     }

 }

Of course there is the potential to decouple this MySession class from HttpContext if desired. 当然,如果需要,可以将MySession类与HttpContext分离。


With regards to this: 关于此:

    //(This should be separated somehow, right?) 
    //Create a new instance of the Utility class
    Utility utility = new Utility();

Yes, anytime you create a new object you are tightly coupling them together, which will give you issues, for example, if you try to unit test it in isolation. 是的,无论何时创建new对象,您都将它们紧密地耦合在一起,这会给您带来问题,例如,如果您尝试对它进行单元测试。

In this instance you could extract an IUtility interface from Utility : 在这种情况下,您可以从Utility提取IUtility接口:

public class Utility : IUtility
{
    string GetWindowsUser();
}

Then: 然后:

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    // ... other fields

    private IUtility utility;

    public DropDownDataRepository(IUtility utility)
    {
        this.utility = utility;
        // .... etc

Then you have removed the depenedency between Utility and DropDownDataRepository , and can substitute in another type or mock with ease. 然后,您已删除了UtilityDropDownDataRepository之间的依赖关系,并且可以轻松替换为其他类型或模拟。

I got a lot of help from Nikolai and Brent and got most of the way there with their posted answers, but ended up figuring out the complete answer on my own. 我从尼古拉(Nikolai)和布伦特(Brent)那里得到了很多帮助,并且在他们发布的答案中得到了大部分帮助,但是最终我自己弄清了完整答案。 The problems I was having were related to not being able to access session variables in a WebAPI. 我遇到的问题与无法访问WebAPI中的会话变量有关。 So, I'm sure there are cleaner solutions to this, but I definitely improved what I had and came up with the following code, which works. 因此,我确定对此有更清洁的解决方案,但是我绝对可以改善自己的工作,并提出了以下有效的代码。

This answer was needed to allow access to the session variable in Web Api - Accessing Session Using ASP.NET Web API 需要此答案才能允许访问Web Api中的会话变量- 使用ASP.NET Web API访问会话

GLOBAL.asax.cs GLOBAL.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
        UnityConfig.RegisterComponents();
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    //Added to allow use of session state in Web API
    protected void Application_PostAuthorizeRequest()
    {
        if (IsWebApiRequest())
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }
    }

    //Added to allow use of session state in Web API
    private bool IsWebApiRequest()
    {
        return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
    }

    protected void Session_Start(Object sender, EventArgs e)
    {
        //Default set the session variable to none
        Session["_impersonatedUser"] = "none";
    }

    protected void Session_End(Object sender, EventArgs e)
    {
        //Reset the session variable to blank
        Session["_impersonatedUser"] = "";
    }
}

UNITY.config UNITY.config

public static class UnityConfig
{
    public static void RegisterComponents()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // e.g. container.RegisterType<ITestService, TestService>();


        container.RegisterType<IDropDownDataRepository, DropDownDataRepository>();
        container.RegisterType<IUtilityRepository, UtilityRepository>();
        container.RegisterType<ISessionRepository, SessionRepository>();

        //MVC5
        //Unity.MVC5 NuGet Package
        DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));

        //WEB API 
        //Unity.WebApi NuGet Package
        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
    }
}

WEB API CONTROLLER Web API控制器

public class DropDownDataController : ApiController
{
    private IDropDownDataRepository _dropDownDataRepository;      

    //Dependency Injection using Unity.WebAPI NuGet Package
    public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
    {
        _dropDownDataRepository = dropDownDataRepository;
    }

    [HttpGet]
    public HttpResponseMessage MyList()
    {
        try
        {
            var sourceQuery = _dropDownDataRepository.MyList();

            //JSON stuff moved to controller
            string result = JsonConvert.SerializeObject(sourceQuery);
            var response = new HttpResponseMessage();
            response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");

            return response;
        }
        catch
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }

    protected override void Dispose(bool disposing)
    {
        _dropDownDataRepository.Dispose();
        base.Dispose(disposing);
    }
}

DROPDOWNDATA REPOSITORY 下拉数据存储库

public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
    private DatabaseEntities db = new DatabaseEntities();
    private IUtilityRepository _utilityRepository;
    private ISessionRepository _sessionRepository;

    //Dependency Injection of Utility and Session
    public DropDownDataRepository(IUtilityRepository utilityRepository, ISessionRepository sessionRepository)
    {
        _utilityRepository = utilityRepository;
        _sessionRepository = sessionRepository;
    }

    //Changed to a list here
    public List<MyProcedure> MyList()
    {
        string windowsUser;
        //Check the session variable to see if a user is being impersonated
        string impersonatedUser = _sessionRepository.ImpersonatedUser;

        //Grab the windowsUser from the Utility Repository
        windowsUser = _utilityRepository.GetWindowsUser();

        if (impersonatedUser != "none")
        {
            windowsUser = impersonatedUser;
        }        

        //Pass windowsUser parameter to the procedure
        var sourceQuery = (from p in db.MyProcedure(windowsUser)
                           select p).ToList();

        return sourceQuery;            
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

DROPDOWNDATA INTERFACE 下拉数据接口

public interface IDropDownDataRepository : IDisposable
{
    //Changed to list here
    List<MyProcedure> MyList();
}

UTILITY REPOSITORY 实用程序存放区

public class UtilityRepository : IUtilityRepository
{
    public string GetWindowsUser()
    {
        //Get the current windows user
        string windowsUser = HttpContext.Current.User.Identity.Name;

        return windowsUser;
    }
}

UTILITY INTERFACE 实用界面

public interface IUtilityRepository
{
    string GetWindowsUser();
}

SESSION REPOSITORY 会话存储库

 public class SessionRepository : ISessionRepository
{
    public string ImpersonatedUser
    {
        get
        {
            return HttpContext.Current.Session["_impersonatedUser"].ToString();
        }
    }
}

SESSION INTERFACE 会话界面

public interface ISessionRepository
{
    string ImpersonatedUser { get; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Windows身份验证模拟MVC应用程序中的Active Directory用户 - Impersonate a Active Directory user in MVC application with Windows Authentication 核心 2.0 - 模拟 Windows 用户的最佳方式? - core 2.0 - best way to impersonate a Windows user? 如何在asp.net中识别impersonate =“true”时获取Windows用户名? - How to get Windows user name when identity impersonate=“true” in asp.net? 模拟 SQL 连接字符串的用户 c# MVC - Impersonate user for SQL connection strings c# MVC 在标准ASP.NET MVC安装模板中模拟用户 - Impersonate a user in a standard ASP.NET MVC installation template 如何在ASP.NET MVC网站上为用户使用Windows身份验证,以及如何在后端WCF服务上使用AppPool用户模拟用户? - How can I use Windows Authentication for Users on a ASP.NET MVC site and impersonate the user using the AppPool user on back-end WCF services? 让IIS在Intranet环境中模拟Windows用户到SQL服务器 - Getting IIS to impersonate the windows user to SQL server in an intranet environment 使用表单授权模拟用户 - Impersonate User with Forms Authorization 模拟一个请求(Asp.net MVC) - Impersonate for one request (Asp.net MVC) Azure AD身份验证-如何模拟任何用户? - Azure AD authentication - How to impersonate any user?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM