C# Can';t在Web API中使用外部身份验证提供程序创建新用户(Visual Studio 2013更新2)
问题: 我正在尝试使用默认的WebAPI模板项目进行外部身份验证。我使用以下说明添加了对外部身份验证服务(FB/Google/Microsoft)的支持: 作为记录,我能够使用默认SPA模板项目进行外部身份验证。 此外,新的本地用户创建工作正常 当我尝试使用我的客户端应用程序(基于WPF)使用外部提供商(如FB)注册用户时,问题就出现了 为了记录在案,我使用这两篇文章作为起点:在Stack Overflow中使用thread#21065648。 他们真的帮助我理解了整个逻辑 以下是我所做步骤的简要概述: 两个窗口,一个用于外部身份验证提供程序,一个用于嵌入式WebBrowser 流程: 2.1. 用户打开一个应用程序,主窗口出现 2.2. 用户单击按钮以获取所有受支持的外部身份验证提供程序的列表, 下面是代码中发生的情况:C# Can';t在Web API中使用外部身份验证提供程序创建新用户(Visual Studio 2013更新2),c#,asp.net,facebook,authentication,asp.net-web-api,C#,Asp.net,Facebook,Authentication,Asp.net Web Api,问题: 我正在尝试使用默认的WebAPI模板项目进行外部身份验证。我使用以下说明添加了对外部身份验证服务(FB/Google/Microsoft)的支持: 作为记录,我能够使用默认SPA模板项目进行外部身份验证。 此外,新的本地用户创建工作正常 当我尝试使用我的客户端应用程序(基于WPF)使用外部提供商(如FB)注册用户时,问题就出现了 为了记录在案,我使用这两篇文章作为起点:在Stack Overflow中使用thread#21065648。 他们真的帮助我理解了整个逻辑 以下是我所做步骤的简
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
然后解析该地址,并以结构化形式保存地址参数(令牌等),以供以后使用
之后,我转到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
}
带来null,因此系统无法使用提供的承载令牌正确地进行身份验证
我不明白为什么。看起来框架中有些东西坏了,或者我做错了什么
解决方案:
感谢@berhir,以下是解决方案:
CookieContainer cookieContainer;
// we create new cookie container
cookieContainer = new CookieContainer();
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");
[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;
}
this.cookieContainer=result.cookieContainer;//其中result是AuthResult,其中包含从WebBrowser会话获得的cookie
使用(var handler=new-HttpClientHandler(){CookieContainer=CookieContainer})
使用(var client=newhttpclient(handler){BaseAddress=BaseAddress})
{
//发送请求
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);
//设置从身份验证提供程序获取的承载令牌
client.SetBearerToken(result.AccessToken);
//调用/api/Account/UserInfo
var userInfoResponse=await client.GetAsync(“api/Account/UserInfo”);
var userInfo=Newtonsoft.Json.JsonConvert.DeserializeObject(userInfoMessage);
if(userInfo.hasRegistered==false)
{
var data=newdictionary();
data.Add(“Email”,this.externalUserEmailTextBox.Text);
var registerexternalur=新Uri(string.Concat(baseAddress,@“api/Account/RegisterExternal”);
var内容=新的FormUrlEncodedContent(数据);
var response=client.PostAsync(registerexternalur.ToString(),content).Result;
//获取内容
var responseContent=response.Content.ReadAsStringAsync().Result;
if(response!=null&&response.issucessStatusCode)
{
MessageBox.Show(“新用户注册,带有“+result.ProviderName+”帐户”);
}
好了,我们开始吧。从第一次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
}
Bernd,这太神奇了!我做了以下工作:1.在MainWindow.xaml.cs中定义CookieContainer,并在MainWindow_加载的事件中实例化它2.一旦用户单击“显示外部提供者”按钮,该CookieContainer将根据代码3分配给新创建的HttpClient。一旦用户选择登录提供者并单击“登录”,调用InternetSetCookie以将所有现有Cookie添加到WebBrowser 4。经过身份验证后,InternetGetCookieEx用于从WebBrowser 5获取所有Cookie。这些Cookie用于调用api/Account/UserInfo和api/Account/RegisterExternal
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");
[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;
}
// set cookies
var cookies = cookieContainer.GetCookies(baseAddress).OfType<Cookie>().ToList();
foreach (var cookie in cookies)
{
InternetSetCookie(startUrl, cookie.Name, cookie.Value);
}
if (this.callback != null)
{
var cookies = GetUriCookieContainer(e.Uri);
this.callback(new AuthResult(e.Uri, this.providerName, cookies));
}
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");
}
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
// send request
}