在Winforms中为所有线程设置CurrentPrincipal

时间:2011-01-04 10:42:07

标签: .net vb.net winforms multithreading

在.NET 3.5 Winforms应用程序中,在用户提供用户名和密码后,我在CurrentPrincipal属性中设置了一个自定义主体,如下所示:

My.User.CurrentPrincipal = Service.GetPrincipal(username)

这是在使用Invoke调用的方法中完成的,因为原始线程不是UI线程:

Invoke(New Action(AddressOf doLogin))

但是当我点击Winforms应用程序中的某个按钮时,CurrentPrincipal属性已恢复为默认值,即当前的Windows用户。

Dim lPrincipal = My.User.CurrentPrincipal ' not my custom principal

显然,在设置主体时使用Invoke并不能解决问题。是否有另一种方法为应用程序中的所有线程设置CurrentPrincipal属性?

重现问题的源代码:

Imports System.Security.Principal
Imports System.Threading

Public Class Form1

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim lThread As New Thread(AddressOf doLogin)
    lThread.Start()
  End Sub

  Private Sub doLogin()
     Invoke(New Action(AddressOf setPrincipal))
  End Sub

  Private Sub setPrincipal()
     My.User.CurrentPrincipal = New CustomPrincipal
     MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Custom
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Default
  End Sub
End Class

Public Class CustomPrincipal
  Implements IPrincipal

  Public ReadOnly Property Identity() As IIdentity Implements IPrincipal.Identity
    Get
      Return New CustomIdentity()
    End Get
  End Property

  Public Function IsInRole(ByVal role As String) As Boolean Implements IPrincipal.IsInRole
     Return True
  End Function
End Class

Public Class CustomIdentity
  Implements IIdentity

  Public ReadOnly Property AuthenticationType() As String Implements IIdentity.AuthenticationType
    Get
      Return "Custom"
    End Get
  End Property

  Public ReadOnly Property IsAuthenticated() As Boolean Implements IIdentity.IsAuthenticated
    Get
      Return True
    End Get
  End Property

  Public ReadOnly Property Name() As String Implements IIdentity.Name
    Get
      Return "CustomName"
    End Get
  End Property
End Class

2 个答案:

答案 0 :(得分:11)

而不是Thread.CurrentPrincipalMy.User.CurrentPrincipal),请使用AppDomain.SetThreadPrincipal

AppDomain.CurrentDomain.SetThreadPrincipal(principal)

答案 1 :(得分:6)

你正在与.NET框架中称为“调用上下文”的东西进行斗争。我需要挥动一下手,因为我完全不了解它。基本前提是当前的主体是.NET安全的大交易。它使沙箱工作,帮助隔离代码,使其不能做任何不安全的事情。代码在浏览器,手机,插件中运行,等等。

当前主体与线程Thread.CurrentPrincipal属性相关联。这使得Control.Begin / Invoke()变得棘手,某种插件可以通过使用它在该线程上运行代码来劫持程序主线程的权限。呼叫上下文是针对此的反制措施。您的代码正在更新调用上下文的主体,而不是线程。在调用的调用完成后,它位于bit桶中,在Control.Invoke()的情况下,它不会流回原始线程。

为此找到一个解决方法让我感到困惑。它实际上必须是源自设置属性的主线程的代码,不会被调用。我只能想到一个愚蠢的修复,使用一毫秒的Timer:

Private Sub setPrincipal()
    Timer1.Enabled = True
End Sub

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    Timer1.Enabled = False
    My.User.CurrentPrincipal = New CustomPrincipal
    MsgBox(My.User.CurrentPrincipal.Identity.AuthenticationType) ' Custom
End Sub

确实有效。当心可能的比赛。