gpt4 book ai didi

c# - 无法使用Web API中的外部身份验证提供程序创建新用户(Visual Studio 2013 Update 2)

转载 作者:太空宇宙 更新时间:2023-11-03 13:19:41 24 4
gpt4 key购买 nike

问题:

我正在尝试使外部身份验证与默认的Web API模板项目一起使用。我按照以下说明添加了对外部身份验证服务(FB / Google / Microsoft)的支持:http://www.asp.net/web-api/overview/security/external-authentication-services

仅作记录,我就可以使用默认的SPA模板项目进行外部身份验证。
此外,创建新的本地用户也可以。

一旦我尝试使用客户端应用程序(基于WPF)通过外部提供商(例如FB)注册用户,就会出现问题。

作为记录,我以这两篇文章作为起点:http://leastprivilege.com/2013/11/26/dissecting-the-web-api-individual-accounts-templatepart-3-external-accounts/和此处的线程#21065648(位于Stack Overflow)。
他们确实帮助我了解了整个逻辑。

这是我已完成的步骤的简短概述:
两个窗口,主窗口和一个用于外部身份验证提供程序的窗口,带有嵌入式WebBrowser
流程:
2.1。用户打开一个应用程序,出现主窗口
2.2。用户单击按钮以获取所有受支持的外部身份验证提供程序的列表,
这是代码中发生的事情:

            var client = new HttpClient();

client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

HttpResponseMessage loginResponse = await client.GetAsync("api/Account/ExternalLogins?returnUrl=%2F&generateState=true");
if (loginResponse.IsSuccessStatusCode)
{
var externalLoginProviders = await loginResponse.Content.ReadAsAsync<IEnumerable<AuthenticationProvider>>();

// cleaning resources
client.Dispose();
loginResponse.Dispose();

// obtained data is sent to UI


这将导致带有名称,URL和状态的外部身份验证提供程序列表。网址是相对的,用作重定向到实际提供程序(例如FB或Google)的URL。

2.3。一旦填写了带有外部身份验证提供程序的列表,用户就可以输入他/她的电子邮件,然后单击“登录”按钮,这将导致以下结果:

在带有嵌入式WebBrowser的第二个窗口中,将后者导航到上一步中提供的URL。如果用户成功登录到选定的提供商(例如Facebook),并同意授予我的应用程序(Facebook应用程序)必要的权限(个人资料),则该用户将以以下形式导航回我们的网站:

            (our base url) /#access_token=XXX&token_type=bearer&expires=YYY


然后解析该地址,并将地址参数(令牌等)以结构化形式保存以备后用。

之后,我进入api / UserInfo了解用户是否已经使用此外部身份验证提供程序登录:

            var client = new HttpClient();

client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

// setting Bearer Token obtained from Auth provider
client.SetBearerToken(result.AccessToken);

// calling /api/Account/UserInfo
var userInfoResponse = await client.GetAsync("api/Account/UserInfo");
var userInfoMessage = userInfoResponse.Content.ReadAsStringAsync().Result;

var userInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfo>(userInfoMessage);

// cleaning resources
client.Dispose();
userInfoResponse.Dispose();

if (userInfo.hasRegistered == true)
{
// going to login
}


此后,假设我们的用户未基于其在给定外部身份验证提供程序(例如FB)上的登录名来创建用户,我们将执行以下操作:

            var data = new Dictionary<string, string>();
data.Add("Email", this.externalUserEmailTextBox.Text);

var registerExternalUrl = new Uri(string.Concat(baseAddress, @"api/Account/RegisterExternal"));

var client = new HttpClient();

client.BaseAddress = new Uri(baseAddress);

// setting Bearer Token obtained from External Authentication provider
client.SetBearerToken(result.AccessToken);

var response = client.PostAsync(registerExternalUrl.ToString(), new FormUrlEncodedContent(data)).Result;


在这一点上,应该创建用户(除了在Startup.Auth.cs中取消注释提供者的应用程序ID和应用程序秘密行之外,服务端的更改为零)。

不幸的是,这根本不会发生。相反,我收到“内部服务器错误”,这意味着这一行,

            var info = await Authentication.GetExternalLoginInfoAsync();


带空值,因此系统无法使用提供的Bearer令牌正确进行身份验证。

我不明白为什么。似乎框架中发生了故障,或者我做错了什么...

解:

感谢@berhir,这是解决方案:


在MainWindow.xaml.cs中,定义cookieContainer

        CookieContainer cookieContainer;

在MainWindow_Loaded事件处理程序中,将其实例化:

        // we create new cookie container
cookieContainer = new CookieContainer();

用户要求应用程序显示所有外部登录提供程序后,请使用@berhir的建议代码实例化HttpClient:

        using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
// send request
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

HttpResponseMessage loginResponse = await client.GetAsync("api/Account/ExternalLogins?returnUrl=%2F&generateState=true");

一旦获得外部身份验证提供程序列表,下一步就是显示第二个带有嵌入式WebBrowser的窗口。在这里,您必须声明两个WinAPI调用cookie:

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);

[DllImport("wininet.dll", SetLastError = true)]
public static extern bool InternetGetCookieEx(string url, string cookieName, StringBuilder cookieData, ref int size, Int32 dwFlags, IntPtr lpReserved);

// and

private const Int32 InternetCookieHttponly = 0x2000;

/// <summary>
/// Gets the URI cookie container.
/// </summary>
/// <param name="uri">The URI.</param>
/// <returns></returns>
public static CookieContainer GetUriCookieContainer(Uri uri)
{
CookieContainer cookies = null;
// Determine the size of the cookie
int datasize = 8192 * 16;
StringBuilder cookieData = new StringBuilder(datasize);
if (!InternetGetCookieEx(uri.ToString(), null, cookieData, ref datasize, InternetCookieHttponly, IntPtr.Zero))
{
if (datasize < 0)
return null;
// Allocate stringbuilder large enough to hold the cookie
cookieData = new StringBuilder(datasize);
if (!InternetGetCookieEx(uri.ToString(), null, cookieData, ref datasize, InternetCookieHttponly, IntPtr.Zero))
return null;
}
if (cookieData.Length > 0)
{
cookies = new CookieContainer();
cookies.SetCookies(uri, cookieData.ToString().Replace(';', ','));
}
return cookies;
}

该带有嵌入式浏览器的窗口被实例化,构造函数接受几个参数,包括开始URL(到外部身份验证提供程序),结束URL(导致“ #access_token = ...”或“ error ...”),回调,甚至更重要的是,使用原始cookieContainer。我们使用InternetSetCookie WinAPI方法将原始cookieContainer传递到WebBrowser的会话:

    // set cookies
var cookies = cookieContainer.GetCookies(baseAddress).OfType<Cookie>().ToList();
foreach (var cookie in cookies)
{
InternetSetCookie(startUrl, cookie.Name, cookie.Value);
}

因此,一旦用户成功登录到所选的外部身份验证提供程序(例如,Facebook),就会使用InternetGetCookieEx获得更新的cookieContainer(包括在第一个HttpClient调用中设置的cookie,以及在登录外部身份验证提供程序后立即在WebBrowser中设置的cookie)。 WinAPI调用通过回调发送回MainWindow.xaml.cs:

        if (this.callback != null)
{
var cookies = GetUriCookieContainer(e.Uri);

this.callback(new AuthResult(e.Uri, this.providerName, cookies));
}

在那里,我们向api / Account / UserInfo和api / Account / RegisterExternal发出两个新请求:

        this.cookieContainer = result.CookieContainer; // where result is AuthResult containing the cookies obtained from WebBrowser's session

using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
// send request
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

// setting Bearer Token obtained from Auth provider
client.SetBearerToken(result.AccessToken);

// calling /api/Account/UserInfo
var userInfoResponse = await client.GetAsync("api/Account/UserInfo");

var userInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfo>(userInfoMessage);

if (userInfo.hasRegistered == false)
{
var data = new Dictionary<string, string>();
data.Add("Email", this.externalUserEmailTextBox.Text);

var registerExternalUrl = new Uri(string.Concat(baseAddress, @"api/Account/RegisterExternal"));

var content = new FormUrlEncodedContent(data);

var response = client.PostAsync(registerExternalUrl.ToString(), content).Result;

// obtaining content
var responseContent = response.Content.ReadAsStringAsync().Result;

if (response != null && response.IsSuccessStatusCode)
{
MessageBox.Show("New user registered, with " + result.ProviderName + " account");
}



所以,我们开始。从第一个HttpClient请求到我们使用api / account / registerExternal注册新用户的最后一刻,Cookie会在整个生命周期中使用。

最佳答案

看来您忘了处理cookie。最简单的方法是对所有请求使用相同的HttpClient实例,也可以使用相同的CookieContainer。

var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
// send request
}

关于c# - 无法使用Web API中的外部身份验证提供程序创建新用户(Visual Studio 2013 Update 2),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24790111/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com