【问题标题】:Worker queue and user context工作队列和用户上下文
【发布时间】:2013-05-16 07:32:26
【问题描述】:

我们有一个工作队列,用户可以向其中添加工作。添加工作项时,上下文是用户 (HttpContext)。但它是一个后台线程,轮询队列并按顺序逐项执行。

我不能只存储用户,因为当 HttpContext 被释放时,Principal 对象也会被释放

可以在 worker 中运行的代码需要 Principal 对 PrincipalPermissions 等内容是正确的。

此外,生命周期管理 (IoC) 将 HttpContext 用于 InRequest 范围,是否可以使用正确的主体等重新创建 HttpContext

编辑: 伪造 HttpContext 只是一个很好的生命时间管理功能,我可以解决这个问题。 但是我们的后端代码很大程度上依赖于线程的正确用户主体,因为我们使用它来验证用户是否可以访问系统的该部分。 如果有人可以回答如何存储具有身份、角色和 IsAuthenticated 状态的用户主体,然​​后在另一个线程上使用它,我会标记为答案

【问题讨论】:

    标签: c# asp.net httpcontext userprincipal principalpermission


    【解决方案1】:

    使用来自HttpContext 的有状态数据的最佳做法是创建您自己的应用程序特定上下文,该上下文在构造函数中接受HttpContext(依赖注入)。

    您的业务逻辑不应依赖于 HttpContext,而应依赖于您的新应用程序特定上下文(可能是使用来自 HttpContext 的信息创建的)。

    这不仅可以解决您的上述问题,还可以增加代码的可测试性。

    例子:

    public class MyApplicationContext
    {
        public IPrincipal ContextPrincipal { get; set; }
    
        public MyApplicationContext(HttpContext httpContext)
        {
            // Store the current user principal & identity
            ContextPrincipal = httpContext.User;
    
            // Need to grab anything else from the HttpContext? Do it here! 
            // That could be cookies, Http request header values, query string 
            // parameters, session state variables, etc.
            //
            // Once you gather up any other stateful data, store it here in 
            // your application context object as the HttpRequest can't be passed 
            // to another thread.
        }
    
    }
    
    public class MyHttpHandler : IHttpHandler
    {
        #region IHttpHandler Members
    
        public bool IsReusable
        {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get { return true; }
        }
    
        public void ProcessRequest(HttpContext context)
        {
            // Do some work on another thread using the ThreadPool
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
        }
    
        public void DoWork(object state)
        {
            // Grab our state info which should be an instance of an 
            // MyApplicationContext.
            MyApplicationContext context = (MyApplicationContext) state;
    
            // Assign this ThreadPool thread's current principal according 
            // to our passed in application context.
            Thread.CurrentPrincipal = context.ContextPrincipal;
    
            // Check if this user is authenticated.
            if (context.ContextPrincipal.Identity.IsAuthenticated)
            {
                var userName = context.ContextPrincipal.Identity.Name;
            }
    
            // Check if this user is an administrator.
            if (context.ContextPrincipal.IsInRole("Administrator"))
            {
            }
    
            // Do some long-ish process that we need to do on the threadpool 
            // after the HttpRequest has already been responded to earlier.
            //
            // This would normally be some fancy calculation/math, data 
            // operation or file routines.
            for (int i = 0; i < 30; i++)
            {
                Thread.Sleep(1000);
            }
        }
    
        #endregion
    }
    

    IPrincipalIIdentity 接口均未明确提供 dispose 方法。所以他们都应该可以保留对他们的引用。不过上面的代码我没有测试过,就是为了这个问题写的。

    如果由于某些糟糕的设计,它们实际上确实依赖于底层数据库连接来查询角色成员资格,那么您只需在应用程序上下文的构造函数中更早地评估它,而 HttpContext 和 asp.net 表单身份验证提供程序是仍未处置/关闭。

    您始终可以拆分主体和身份并重新创建GenericPrincipalGenericIdentity 的新实例,甚至可以创建实现IIdentity 的应用程序身份类。这里有很大的定制/扩展空间。

    【讨论】:

    • 问题在于它的线程睡眠循环中的代码依赖于主体,一旦 HttpContext 被释放,主体就会失败。为了澄清我们的业务逻辑对 System.Web 类没有任何依赖。但它使用 Thread.CurrentPrincipal 引擎盖下使用 HttpContext (如果代码在 IIS 下运行)。
    • @Anders:再次查看代码。我在上面做的第一件事就是在新的 ThreadPool 线程中分配Thread.CurrentPrincipal!我还解释说,主体本身没有被处置——唯一可能的解释是,填充主体角色列表的底层提供者被处置,不允许您检查角色。也许你应该发布你得到的异常的堆栈跟踪?无论哪种方式,只需提前进行这些检查并将其存储在您的应用程序上下文中 - 或编写您自己的 IIdentity 来存储更多信息。
    【解决方案2】:
    public void TestMethod1()
    {
        System.Net.WebClient client = new System.Net.WebClient();
        client.BaseAddress = "http://www.teejoo.com";            
    
        //Invoke your function here
        client.OpenReadAsync(new Uri("http://www.teejoo.com/YourLogicalPage.aspx"));
        //Pur your logical in your page, so you can use httpContext 
    
        client.OpenReadCompleted += new System.Net.OpenReadCompletedEventHandler(client_OpenReadCompleted);
    }
    
    void client_OpenReadCompleted(object sender, System.Net.OpenReadCompletedEventArgs e)
    {            
        //to Check the response HERE
    }
    

    【讨论】:

    • 这不是我想要的,我不想创建一个 http 请求,我想为一个没有的后台工作人员创建一个 httpcontext。其实最重要的是我可以以某种方式存储和重用 HttpContext 的 Principal
    • 您不能在后台创建 HttpContext,因此您可以通过创建 httpRequest 并访问应用程序中的页面来解决此问题
    • 你的逻辑页面中有一个HttpContext。
    • 为什么要尝试克隆 Principal 对象,您可以使用用户提供的主体对象开始新的“伪造 httpcontext”,在“THIS”请求线程中开始后台工作,然后设置你的 client.principal = this.request.principal
    • 你不能,因为当 HttpContext 是时该主体被释放
    【解决方案3】:

    您为什么不使用辅助类来保存您需要的信息?您可以在 Web 请求期间使用适当的值创建它,并将其作为参数传递给后台工作人员。

    由于内部server session state,无法克隆HTTPContext 对象。即使有可能,在真正的 HTTP 请求之外使用它来检查值似乎也不是一个好的解决方案。

    【讨论】:

    • 最低要求是线程的 Principal 是正确的,因为我们使用它来验证使用 PrincipalAttributes 的方法访问。因此,如果我能以某种方式克隆具有身份、角色和 IsAuthenticated 状态的主体就足够了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    • 2018-06-20
    • 1970-01-01
    • 1970-01-01
    • 2015-04-30
    • 1970-01-01
    • 2016-04-22
    相关资源
    最近更新 更多