在 .Net Core 3.1 中使用重定向和 Cookie 复制 cURL 命令 [英] Replicate cURL Command Using Redirect and Cookies in .Net Core 3.1
问题描述
这似乎是一个远景.但是我看到几个答案表明在 .Net Core 应用程序中需要 cURL 时应该使用 HttpClient(和类似的).
This one seems a long shot. But I have seen several answers that indicate that HttpClient (and similar) should be used when cURL is needed in a .Net Core Application.
我有以下 cURL 命令(效果很好):
I have the following cURL command (that works perfectly):
curl -v -L --negotiate -u : -b ~/cookiejar.txt "https://idp.domain.net/oauth2/authorize?scope=openid&response_type=code&redirect_uri=https://localhost:5001&client_id=client_id_here"
这个命令的流程是这样的:
The flow of this command goes like this:
- 加载提供的 url (https://idp.domain.net/oauth2/授权....)
- 获得 302 响应以重定向到 https://idp.domain.net/iwa-kerberos?state=state_guid_here
- 因为有
-L
选项,所以它遵循重定向
- 因为有
- Loads the provided url (https://idp.domain.net/oauth2/authorize....)
- Gets a 302 response to redirect to https://idp.domain.net/iwa-kerberos?state=state_guid_here
- Because the
-L
option is there, it follows the redirect
- Because the
- 由于
-b
选项,cookie 被 cURL 获取.
- Because of the
-b
option, the cookie is picked up by cURL.
把这一切都写下来,让它在 .Net 应用程序中工作似乎是一项严肃的任务.但我想我会问它是否内置在某个框架中.
Writing this all out, it seems like a serious undertaking to get this to work in a .Net Application. But I figured I would ask in case it is built in to the framework somewhere.
是否有一个 .Net Core Framework 类(或类似的)可以让我在 C# 代码中重现这个 cURL 命令?
注意:我可以通过调用 powershell 来完成.这个问题是关于用 HttpClient
做的.
NOTE: I am able to do it by calling out to powershell. This question is about doing it with HttpClient
.
推荐答案
我能够构建一个自定义用途的方法来完成我需要的调用(以获取 OAuth 2 身份验证代码).
I was able to build a custom purpose method that completed the call I needed (to get an OAuth 2 Auth Code).
Cookies 是自动添加的,但我是否添加了 CookieContainer
和 UseCookies
设置似乎并不重要.
The Cookies got added automatically, but it did not seem to matter if I added the CookieContainer
and UseCookies
settings or not.
此外,UseDefaultCredentials
似乎也没有做任何事情.
Also, UseDefaultCredentials
did not seem to do anything either.
async Task Main()
{
var services = new ServiceCollection();
services.AddHttpClient("OAuthClient").ConfigurePrimaryHttpMessageHandler(() => new AuthenticationHandler());;
var serviceProvider = services.BuildServiceProvider();
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
var authCodeUrl = "https://idp.domain.net/oauth2/authorize?scope=openid&response_type=code&redirect_uri=https://localhost:5001&client_id=client_id_here";
var authNegotiator = new AuthenticaitonNegotiator(httpClientFactory);
var authCode = await authNegotiator.GetAuthorizationCodeViaKerberosIwa(authCodeUrl);
Console.WriteLine(authCode);
}
public class AuthenticaitonNegotiator
{
private IHttpClientFactory httpClientFactory;
public AuthenticaitonNegotiator(IHttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public async Task<string> GetAuthorizationCodeViaKerberosIwa(string authCodeUrl)
{
var kerberosToken = GetKerberosTokenViaIwa();
var authCode = await GetAuthorizationCodeViaKerberos(authCodeUrl, kerberosToken);
return authCode;
}
public async Task<string> GetAuthorizationCodeViaKerberosCredsAsync(string authCodeUrl, string username, string password)
{
var kerberosToken = await GetKerberosTokenViaCredsAsync(username, password);
var authCode = await GetAuthorizationCodeViaKerberos(authCodeUrl, kerberosToken);
return authCode;
}
public async Task<string> GetAuthorizationCodeViaKerberos(string authCodeUrl, string kerberosToken)
{
var httpClient = httpClientFactory.CreateClient("OAuthClient");
var done = false;
string currentUrl = authCodeUrl;
string responseText = "";
bool wasSuccessful = false;
while (!done)
{
var response = await httpClient.GetAsync(currentUrl);
responseText = await response.Content.ReadAsStringAsync();
// Reset the authenticaiton header if it was set. (It gets set as needed on each iteration.)
httpClient.DefaultRequestHeaders.Authorization = null;
if (response.StatusCode == HttpStatusCode.Unauthorized
&& response.Headers.Any(x => x.Key == "WWW-Authenticate" && x.Value.Contains("Negotiate")))
{
currentUrl = response.RequestMessage.RequestUri.AbsoluteUri;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Negotiate", kerberosToken);
}
else if (response.StatusCode == HttpStatusCode.Redirect)
{
var redirectUri = response.Headers.Location;
var query = HttpUtility.ParseQueryString(redirectUri.Query);
var code = query.Get("code");
if (code == null)
{
currentUrl = redirectUri.AbsoluteUri;
}
else
{
// If this is the last redirect where we would send to the callback, just grab the auth code.
// This saves us from needing to host a service to handle the callback.
responseText = code;
done = true;
wasSuccessful = true;
}
}
else
{
done = true;
wasSuccessful = false;
}
}
if (wasSuccessful == false)
{
throw new ApplicationException($"Failed to retrive authorization code:
{responseText}");
}
return responseText;
}
public async Task<String> GetKerberosTokenViaCredsAsync(string username, string password)
{
var client = new KerberosClient();
var kerbCred = new KerberosPasswordCredential(username, password, "YourDomain.net");
await client.Authenticate(kerbCred);
var ticket = await client.GetServiceTicket("http/ServerToGetTheKerberosToken.YourDomain.net");
return Convert.ToBase64String(ticket.EncodeGssApi().ToArray());
}
public string GetKerberosTokenViaIwa()
{
string token = "";
using (var context = new SspiContext($"http/ServerToGetTheKerberosToken.YourDomain.net", "Negotiate"))
{
var tokenBytes = context.RequestToken();
token = Convert.ToBase64String(tokenBytes);
}
return token;
}
}
public class AuthenticationHandler : HttpClientHandler
{
public AuthenticationHandler()
{
// cURL Equivilant: -L
AllowAutoRedirect = true;
MaxAutomaticRedirections = 100;
// cURL Equivilant: --negotiate -u :
UseDefaultCredentials = true;
// cURL Equivilant: -b ~/cookiejar.txt
CookieContainer = new CookieContainer();
UseCookies = true;
}
}
如果您添加以下 NuGet 包,这将在 LinqPad 中运行:
This runs in LinqPad if you add the following NuGet Packages:
- Kerberos.NET
- Microsoft.Extensions.DependencyInjection
- Microsoft.Extensions.Http
我也有以下使用"声明:
I also had the following "using" statements:
系统
System.Collections
System.Collections
System.Collections.Generic
System.Collections.Generic
系统数据
系统诊断
System.IO
System.Linq
System.Linq
System.Linq.Expressions
System.Linq.Expressions
系统反射
系统文本
System.Text.RegularExpressions
System.Text.RegularExpressions
系统线程
系统事务
系统.Xml
System.Xml.Linq
System.Xml.Linq
System.Xml.XPath
System.Xml.XPath
Kerberos.NET
Kerberos.NET
Kerberos.NET.Client
Kerberos.NET.Client
Kerberos.NET.Credentials
Kerberos.NET.Credentials
Kerberos.NET.Entities
Kerberos.NET.Entities
Kerberos.NET.Win32
Kerberos.NET.Win32
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Http
Microsoft.Extensions.Http
System.Net
System.Net.Http
System.Net.Http
System.Net.Http.Headers
System.Net.Http.Headers
System.Threading.Tasks
System.Threading.Tasks
System.Web
这篇关于在 .Net Core 3.1 中使用重定向和 Cookie 复制 cURL 命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!