如何存储WindowsIdentity以便在以后的不确定时间使用

时间:2013-03-06 15:51:29

标签: .net vb.net wcf windows-identity

我有一个WCF服务,它代表用户在稍后的不确定时间排队并执行命令。我希望将WindowsIdentity存储为字节数组并将其填充到数据库中,然后反序列化并使用该对象。

有些时候,服务按预期执行:它正确地序列化,存储,反序列化并以用户身份执行命令。其他时候,我在反序列化WindowsIdentity时遇到错误“调用目标已抛出异常”,有时反序列化工作但在执行命令的过程中,标识不再有效。

我的问题是:在.NET 4.0框架中是否可以使用WCF代表用户在稍后的不确定时间执行命令而没有明确的用户名和密码?

我正在使用的代码如下:

序列化:

''' <summary>
''' Serializes a WindowsIdentity as a binary encoded byte array.
''' </summary>
''' <param name="identity">The identity to serialize.</param>
''' <returns>A byte array containing a binary representation of the identity.</returns>
Private Function SerializeWindowsIdentity(ByVal identity As WindowsIdentity) As Byte()
    If IsNothing(identity) Then Return Nothing
    Try
        Dim bf As New BinaryFormatter
        Using ms As New MemoryStream()
            bf.Serialize(ms, identity)
            Return ms.ToArray()
        End Using
    Catch ex As Exception
        Return Nothing
    End Try
End Function ' SerializeWindowsIdentity

反序列化:

''' <summary>
''' Deserializes a WindowsIdentity from a binary encoded byte array.
''' </summary>
''' <param name="identity">A byte array containing a binary representation of a WindowsIdentity</param>
''' <returns>The deserialized WindowsIdentity from the byte array.</returns>
Private Function DeserializeWindowsIdentity(ByVal identity As Byte()) As WindowsIdentity
    If IsNothing(identity) OrElse identity.Count = 0 Then Return Nothing
    Try
        Dim bf As New BinaryFormatter()
        Using ms As New MemoryStream(identity)
            Dim obj As Object = bf.Deserialize(ms)
            Return CType(obj, WindowsIdentity)
        End Using
    Catch ex As Exception
        Return Nothing
    End Try
End Function ' DeserializeWindowsIdentity

WindowsIdentity捕获:

identity = SerializeWindowsIdentity(ServiceSecurityContext.Current.WindowsIdentity)

用法:

Dim cxt As WindowsImpersonationContext
Try
    Dim wi As WindowsIdentity = DeserializeWindowsIdentity(identity)
    If Not IsNothing(wi) Then cxt = wi.Impersonate()

    ' Do Stuff
Finally
    If Not IsNothing(cxt) Then cxt.Dispose()
End If

2 个答案:

答案 0 :(得分:0)

这是一些伪代码:

    //When command comes in from the user, queue the work
    private void QueueWork()
    {
        //Authorization Check
        if (IsAuthorized(DoWork, ServiceSecurityContext.Current.WindowsIdentity))
            throw new SecurityAccessDeniedException("Unauthorized");

        //Queue the work for later
        Queue.Enqueue(ServiceSecurityContext.Current.WindowsIdentity.Name);
    }

    //check if 
    private bool IsAuthorized(Action doWork, WindowsIdentity windowsIdentity)
    {
        //custom logic
    }

    //Command executed at a later point in time
    private void DoWork()
    {
        var user = Queue.Dequeue() as string;

        Log(user + " is invoking DoWork");

        //Perform command on behalf of user
        //...
    }

它假定您启用了Windows身份验证以捕获WindowsIdentity。

此外,我不确定你是如何排队命令的,以及你以后如何执行它们。

如果您愿意,可以使用其他授权方式替换IsAuthorized方法。

该服务具有执行操作的权限,只需记录正在执行的用户和命令。

如果这不适合您的情况,请告诉我并提供更多信息,我可以修改答案。但希望这能引导你走向正确的方向

答案 1 :(得分:0)

因此,到目前为止,我们所拥有的最佳解决方案是使用ServiceSecurityContext.Current.WindowsIdentity中的令牌并创建一个新的主令牌,我们将其序列化并存储以供日后使用。缺点是一旦服务重新启动,令牌无效,但这是一个很好的临时解决方案,直到我们可以修改我们的服务,以便我们不需要用户的持久授权。我们查看了使用执行我们想要的S4U2Proxy,但是对于我们的用户来说,配置运行该服务的域帐户的要求有点麻烦。下面是工作代码片段(注意:我们可能不需要再序列化,但由于我们不必更新数据库模式,因此更容易保留。此外,我们可以打破序列化例程令牌重复,使代码更易于管理):

反序列化代码:

''' <summary>
''' Deserializes a user token from a binary encoded byte array.
''' </summary>
''' <param name="identity">A byte array containing an binary representation of a user token.</param>
''' <returns>The deserialized user token from the byte array.</returns>
Private Function DeserializeWindowsIdentityToken(ByVal identity As Byte()) As IntPtr
    If IsNothing(identity) Then Return Nothing
    Dim stream As New MemoryStream(identity)
    Dim serializer As New BinaryFormatter()
    Try
        Dim obj As Object = serializer.Deserialize(stream)
        Return CType(obj, IntPtr)
    Catch ex As Exception
        Return IntPtr.Zero
    End Try
End Function ' DeserializeWindowsIdentityToken

序列化代码:

''' <summary>
''' Serializes a user token as a binary encoded byte array.
''' </summary>
''' <param name="identity">The token to serialize.</param>
''' <returns>A byte array containing a binary representation of the token.</returns>
Private Function SerializeWindowsIdentityToken(ByVal identity As IntPtr) As Byte()
    Try
        Dim newToken As IntPtr = IntPtr.Zero
        Const securityDelegation As Int16 = 3
        Const tokenPrimary As Integer = 1
        Const maximumAllowed As Integer = &H2000000

        Dim sa As New SecurityAttributes()
        sa.bInheritHandle = True
        sa.Length = Marshal.SizeOf(sa)
        sa.lpSecurityDescriptor = IntPtr.Zero

        If DuplicateTokenEx(identity, maximumAllowed, sa, securityDelegation, tokenPrimary, newToken) = 0 Then Return Nothing

        Dim streamWriter As New MemoryStream()
        Dim serializer As New BinaryFormatter
        serializer.Serialize(streamWriter, newToken)
        Return streamWriter.ToArray()
    Catch ex As Exception
        Return Nothing
    End Try
End Function ' SerializeWindowsIdentityToken

<StructLayout(LayoutKind.Sequential)>
Private Structure SecurityAttributes
    Public Length As Integer
    Public lpSecurityDescriptor As IntPtr
    Public bInheritHandle As Boolean
End Structure ' SecurityAttributes

<DllImport("advapi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function DuplicateTokenEx(ByVal existingTokenHandle As IntPtr,
                                         ByVal desiredAccess As UInteger,
                                         ByRef threadAttributes As SecurityAttributes,
                                         ByVal impersonationLevel As Integer,
                                         ByVal tokenType As Integer,
                                         ByRef duplicateTokenHandle As IntPtr) As Integer
End Function ' DuplicateTokenEx

令牌捕获:

Dim storedToken As Byte() = SerializeWindowsIdentityToken(ServiceSecurityContext.Current.WindowsIdentity.Token)

用法:

Dim identity As IntPtr = DeserializeWindowsIdentityToken(storedToken)
Dim cxt As WindowsImpersonationContext = Nothing
If Not IsNothing(identity) AndAlso identity <> IntPtr.Zero Then
    Try
        Dim identity As New WindowsIdentity(identity)
        cxt = identity.Impersonate()
    Catch ex As Exception
        ' Perform error handling
    End Try
End If

' Perform operations

If Not IsNothing(cxt) Then cxt.Dispose()