. Net Ядро подделки WindowsIdentity не работает - PullRequest
4 голосов
/ 25 марта 2020

У меня есть следующий код:

var baseUrl = "https://" + GetIdentityProviderHost(environment) + "/oauth2/authorize";
var query = $"?scope=openid&response_type=code&redirect_uri={redirectUrl}&client_id={clientId}";
var combinedUrl = baseUrl + query;

var currentUser = WindowsIdentity.GetCurrent(); 

await WindowsIdentity.RunImpersonated(currentUser.AccessToken, async() =>
{
    using (var client = new WebClient{ UseDefaultCredentials = true })
    {
        var response = client.DownloadString(combinedUrl);          
        Console.WriteLine(response);
    }
});

Он в основном создает URL-адрес и затем вызывает его.

Вызов возвращается с 401 (неавторизованным).

Но если я возьму combinedUrl и вставлю его в chrome или почтальона, он будет отлично работать . Это говорит о том, что мой вызов может работать, потому что Chrome использует мои Windows учетные данные для выполнения вызова.

Я добавил код WindowsIdentity.RunImpersonated, чтобы попытаться обойти эту проблему. Но, похоже, это никак не повлияло.

Как мне сделать веб-вызов с использованием встроенной Windows аутентификации (IWA)?


Подробности:

Если я запускаю следующую команду cURL, она работает:

curl -L --negotiate -u : -b ~/cookiejar.txt "https://myIdp.domain.net/oauth2/authorize?scope=openid&response_type=code&redirect_uri=https://localhost:5001&client_id=my_client_id_here"

Я не уверен, как воспроизвести все это в C# коде.

К вашему сведению: у меня есть спросил об этой команде cURL именно в этом вопросе (поскольку этот вопрос был сфокусирован на олицетворении): Репликация команды cURL с использованием перенаправления и файлов cookie в. Net Core 3.1

Ответы [ 3 ]

1 голос
/ 03 апреля 2020

К сожалению, я не смог воспроизвести вашу проблему, олицетворение работает нормально для меня при использовании этого кода:

WindowsIdentity identity = WindowsIdentity.GetCurrent();

using (identity.Impersonate())
{
    HttpWebRequest request = (HttpWebRequest) WebRequest.Create("https://my-address");
    request.UseDefaultCredentials = true;

    HttpWebResponse response = (HttpWebResponse) request.GetResponse();
}

Я проверял это с помощью. NET Только Framework, но так как вы уже попытался настроить Credentials вручную, я думаю, это не проблема. NET Основная проблема подражания, о которой упоминалось в одном из комментариев.

Так что я думаю, что проблема связана с адресом, который вы пытаетесь для доступа.

Возможно, проблема в перенаправлении, которое я не смог проверить, но вы можете попробовать решение из этого ответа. Вы должны использовать request.AllowAutoRedirect = false, поскольку значением по умолчанию является true, и в этом случае заголовок авторизации очищается при автоматическом перенаправлении (MSDN Свойство AllowAutoRedirect ).

В противном случае вы можете также хотите попробовать использовать request.ImpersonationLevel = TokenImpersonationLevel.Delegation (MSDN ImpersonationLevel Property ) или request.PreAuthenticate = true (MSDN PreAuthenticate Property ).

Как я уже сказал, я не смог воспроизведите проблему, так что это всего лишь некоторые идеи, которые могут (или не могут) работать на вас ...

1 голос
/ 01 апреля 2020

У меня нет коробки Windows передо мной, поэтому я не могу проверить это полностью. Но это похоже на змеиную яму, основанную на обсуждении, например, здесь (особенно из этого комментария внизу): https://github.com/dotnet/runtime/issues/24009#issuecomment -544511572

Кажется, существуют различные мнения о как сохранить идентичность в асинхронном c вызове.

, но если вы посмотрите на пример в этом комментарии,

app.Use(async (context, next) =>
{
    await WindowsIdentity.RunImpersonated(someToken, () => next());
});

это не выглядит весело c второй аргумент WindowsIdentity.RunImpersonated должен быть асин c.

Вы пробовали:

var baseUrl = "https://" + GetIdentityProviderHost(environment) + "/oauth2/authorize";
var query = $"?scope=openid&response_type=code&redirect_uri={redirectUrl}&client_id={clientId}";
var combinedUrl = baseUrl + query;

var currentUser = WindowsIdentity.GetCurrent(); 

await WindowsIdentity.RunImpersonated(currentUser.AccessToken, () =>
{
    using (var client = new WebClient{ UseDefaultCredentials = true })
    {
        var response = client.DownloadString(combinedUrl);          
        Console.WriteLine(response);

    }
});

Документы Microsoft можно найти на WindowsIdentity.RunImpersonated здесь: https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.runimpersonated?view=netcore-3.1

0 голосов
/ 06 апреля 2020

Ваш код подражания в порядке. Вы пишете windows приложение или Asp. net базовое приложение? Возможно, существует проблема с учетной записью пользователя, так как пользователь, под которым вы выполняете код, является стандартным пользователем и не может выдавать себя за пользователя. Попробуйте пользователя домена и предоставьте права администратора для тестирования. Другая проблема заключается в том, что он может использоваться только в интерактивном режиме. Как код для консольного приложения. // В следующем примере демонстрируется использование класса WindowsIdentity для олицетворения пользователя.
// ВАЖНОЕ ПРИМЕЧАНИЕ:
// В этом примере пользователю предлагается ввести пароль на экране консоли.
// пароль будет виден на экране, потому что окно консоли
// изначально не поддерживает маскированный ввод.

using System;  
using System.Runtime.InteropServices;  
using System.Security;  
using System.Security.Principal;  
using Microsoft.Win32.SafeHandles;  

public class ImpersonationDemo  
{  
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,  
        int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);  

    public static void Main()  
    {  
        // Get the user token for the specified user, domain, and password using the   
        // unmanaged LogonUser method.   
        // The local machine name can be used for the domain name to impersonate a user on this machine.  
        Console.Write("Enter the name of the domain on which to log on: ");  
        string domainName = Console.ReadLine();  

        Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);  
        string userName = Console.ReadLine();  

        Console.Write("Enter the password for {0}: ", userName);  

        const int LOGON32_PROVIDER_DEFAULT = 0;  
        //This parameter causes LogonUser to create a primary token.   
        const int LOGON32_LOGON_INTERACTIVE = 2;  

        // Call LogonUser to obtain a handle to an access token.   
        SafeAccessTokenHandle safeAccessTokenHandle;  
        bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),  
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,  
            out safeAccessTokenHandle);  

        if (false == returnValue)  
        {  
            int ret = Marshal.GetLastWin32Error();  
            Console.WriteLine("LogonUser failed with error code : {0}", ret);  
            throw new System.ComponentModel.Win32Exception(ret);  
        }  

        Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));  
        // Check the identity.  
        Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);  

        // Note: if you want to run as unimpersonated, pass  
        //       'SafeAccessTokenHandle.InvalidHandle' instead of variable 'safeAccessTokenHandle'  
        WindowsIdentity.RunImpersonated(  
            safeAccessTokenHandle,  
            // User action  
            () =>  
            {  
                // Check the identity.  
                Console.WriteLine("During impersonation: " + WindowsIdentity.GetCurrent().Name);  
            }  
            );  

        // Check the identity again.  
        Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);  
    }  
}
...