23

我正在尝试解决将 Google 用作外部登录提供程序时出现的间歇性问题。

尝试登录时,用户将被重定向回登录页面,而不是进行身份验证。

问题出现在这一行(下面链接的第 55 行),GetExternalIdentityAsync 返回 null。

var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

完整的代码是:

[Authorize]
public abstract class GoogleAccountController<TUser> : Controller where TUser : Microsoft.AspNet.Identity.IUser
{
    public IAuthenticationManager AuthenticationManager
    {
        get
        {
            return HttpContext.GetOwinContext().Authentication;
        }
    }

    public abstract UserManager<TUser> UserManager { get; set; }

    [AllowAnonymous]
    [HttpGet]
    [Route("login")]
    public ActionResult Login(string returnUrl)
    {
        ViewData.Model = new LoginModel()
        {
            Message = TempData["message"] as string,
            Providers = HttpContext.GetOwinContext().Authentication.GetExternalAuthenticationTypes(),
            ReturnUrl = returnUrl
        };

        return View();
    }

    [AllowAnonymous]
    [HttpPost]
    [ValidateAntiForgeryToken]
    [Route("login")]
    public ActionResult Login(string provider, string returnUrl)
    {
        return new ChallengeResult(provider, Url.Action("Callback", "Account", new { ReturnUrl = returnUrl }));
    }

    [AllowAnonymous]
    [Route("authenticate")]
    public async Task<ActionResult> Callback(string returnUrl)
    {
        var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);

        if (externalIdentity == null)
        {
            return RedirectToAction("Login", new { ReturnUrl = returnUrl });
        }

        var emailAddress = externalIdentity.FindFirstValue(ClaimTypes.Email);
        var user = await UserManager.FindByNameAsync(emailAddress);

        if (user != null)
        {
            await SignInAsync(user, false);

            return RedirectToLocal(returnUrl);
        }
        else
        {
            TempData.Add("message", string.Format("The account {0} is not approved.", emailAddress));

            return RedirectToAction("Login", new { ReturnUrl = returnUrl });
        }
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    [Route("logout")]
    public ActionResult Logout(string returnUrl)
    {
        AuthenticationManager.SignOut();

        return RedirectToLocal(returnUrl);
    }

    private async Task SignInAsync(TUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        var authenticationProperties = new AuthenticationProperties()
        {
            IsPersistent = isPersistent
        };

        AuthenticationManager.SignIn(authenticationProperties, identity);
    }

    private ActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && UserManager != null)
        {
            UserManager.Dispose();
            UserManager = null;
        }

        base.Dispose(disposing);
    }
}

也在这里

这在很大程度上是一个间歇性问题,重新部署应用程序通常会使其暂时工作。

在 Fiddler 中,我可以看到在它无法找到 cookie 的身份验证方法之前调用了 sign-google。

提琴手截图

该应用程序使用以下代码来初始化谷歌登录

app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/login")
    });
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseGoogleAuthentication();

我已在 web.config 中将身份验证模式设置为非,并删除了表单身份验证模块。

<system.web>
    <authentication mode="None" />
</system.web>    
<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />    
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="FormsAuthenticationModule" />
    </modules>
</system.webServer>

这些站点托管在 Azure 上,一些在 1 个实例上运行,一些在 2 个实例上运行。它们具有自定义域,尽管在自定义域和 azurewebsites 域以及 http / https 上仍然失败。

谁能帮助解释为什么会发生这种情况?

更新

Microsoft.Owin.Security.Google 3.0 版于昨晚发布。打算切换一下,看看这是否能解决问题。

https://www.nuget.org/packages/Microsoft.Owin.Security.Google

4

7 回答 7

3

在无缘无故遇到同样的问题后,我设法通过比较 2 个项目来解决它。一个每次都没有问题的测试项目和我比较的另一个项目。我发现他们有完全相同的代码,但不同的 dll 版本。

Nuget 中引用的包是故障点。

确保您拥有最新的软件包,并检查web.config中的运行时部分。


在我更新了所有 Owin 相关包和 Microsoft.Owin 并添加了:

<assemblyBinding>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
  </dependentAssembly>
</assemblyBinding>

...它再次起作用了!它们可能会根据您使用的软件包而有所不同,但这就是我的工作方式。

于 2015-07-10T11:15:48.260 回答
2

Microsoft 的 System.Web 的 Owin 实现中存在一个错误。在 IIS 上运行 Owin 应用程序时使用的一种。如果我们在 ASP.NET MVC5 中使用新的基于 Owin 的身份验证处理,那么我们 99% 的人可能会这样做。

该错误使 Owin 设置的 cookie 在某些情况下神秘地消失了。

将此 nuget 放在https://github.com/KentorIT/owin-cookie-saver之前 app.UseGoogleAuthentication(...)

于 2016-12-31T20:55:20.433 回答
2

我忘了在谷歌开发者控制台中启用“谷歌+ API”。谷歌登录似乎很好,但 GetExternalLoginInfoAsync 返回 null。

您可以点击此链接 https://stackoverflow.com/a/27631109/657926

于 2016-02-16T11:16:11.297 回答
1

Tom 我通过使用 REST API 在我的 asp.net 应用程序中使用 google-oauth。它工作正常,我没有遇到任何连接问题。

我正在执行以下步骤:

1.我在谷歌开发者控制台中创建了一个项目,我创建了设置“Web 应用程序的客户端 ID”,其中包含以下参数。

a)Client ID => google 自动生成 b)Email address=> google 自动生成 c)Client secret=> google 自动生成 d)Redirect URIs => 需要指定 url用于处理身份验证过程的网页。在这个页面我们可以进行身份​​验证,我们可以获取用户的基本信息。

my url: "http://localhost:1822/WebForm1.aspx/code"

我的用法:

  1. 我创建了一个包含“Webpage1.aspx”和“Webpage2.aspx”的示例项目。

我已经设置了“Webpage2.aspx”启动页面,并且在“Webpage2.aspx”中形成了开放的身份验证 URL 并重定向到谷歌登录页面。

Google Open Auth url 形成

登录后,它会重定向到“Webpage1.aspx”以及访问代码。通过将此访问代码传递给“ https://accounts.google.com/o/oauth2/token ” url,我将获得访问令牌以及令牌类型和令牌到期时间。之后,通过将此访问权传递给“ https://www.googleapis.com/oauth2/v2/userinfo ” url,我将获取用户基本信息,例如“电子邮件、姓名、性别、照片等......)

示例代码

    public class GoogleAuthorizationData
    {
        public string access_token { get; set; }
        public int expires_in { get; set; }
        public string token_type { get; set; }

    }

  public class GoogleUserInfo
    {
        public string name { get; set; }
        public string family_name { get; set; }
        public string gender { get; set; }
        public string email { get; set; }
        public string given_name { get; set; }
        public string picture { get; set; }
        public string link { get; set; }
        public string id { get; set; }

    }

  Webpage1.aspx
  ============
 protected void Page_Load(object sender, EventArgs e)
        {
            string code = Request.QueryString["code"].ToString();
            string scope = Request.QueryString["scope"].ToString();
            string url = "https://accounts.google.com/o/oauth2/token";
            string postString = "code=" + code + "&client_id=" + ConfigurationManager.AppSettings["GoogleClientID"].ToString() + "&client_secret=" + ConfigurationManager.AppSettings["GoogleSecretKey"].ToString() + "&redirect_uri=" + ConfigurationManager.AppSettings["ResponseUrl"].ToString() + "&grant_type=authorization_code";

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.ToString());
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            UTF8Encoding utfenc = new UTF8Encoding();
            byte[] bytes = utfenc.GetBytes(postString);
            Stream os = null;
            try
            {
                request.ContentLength = bytes.Length;
                os = request.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);
            }
            catch
            { }

            try
            {
                HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
                Stream responseStream = webResponse.GetResponseStream();
                StreamReader responseStreamReader = new StreamReader(responseStream);
                var result = responseStreamReader.ReadToEnd();//
                var json = new JavaScriptSerializer();

                GoogleAuthorizationData authData = json.Deserialize<GoogleAuthorizationData>(result);

                HttpWebRequest request1 = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v2/userinfo");
                request1.Method = "GET";
                request1.ContentLength = 0;
                request1.Headers.Add("Authorization", string.Format("{0} {1}", authData.token_type, authData.access_token));
                HttpWebResponse webResponse1 = (HttpWebResponse)request1.GetResponse();
                Stream responseStream1 = webResponse1.GetResponseStream();
                StreamReader responseStreamReader1 = new StreamReader(responseStream1);
                GoogleUserInfo userinfo = json.Deserialize<GoogleUserInfo>(responseStreamReader1.ReadToEnd());
               Response.Write(userinfo.email);

            }
            catch (Exception eX)
            {
                throw eX;
            }





        }
于 2014-05-15T17:39:29.073 回答
1

我也有同样的问题。我正在使用 Visual Studio 2013,网站在 Azure 上。社交登录一直在正常工作而没有问题停止工作,并且 LinkLoginCallback 在 loginInfo 中接收到空值。我在没有更改代码或重建的情况下重新发布了该项目,然后 loginInfo 收到了正确的数据并且一切正常。没有意义,但你去。

于 2014-09-21T23:33:49.727 回答
1

我相信您不应该使用app.UseGoogleAuthentication();它,因为它会尝试使用已弃用的 OpenID 2.0 。
您应该改用OAuth 2.0 for Login (OpenID Connect)
所以:

  1. 在 Google Developers Console 中注册您的应用
  2. 使其能够访问 Google+ API(即使您不打算直接使用 Google+ - 它现在被用作身份验证的手段)
  3. 以这种方式启用 ASP.NET Identity 的 Google 身份验证
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()  
{  
    ClientId = "YOUR_CLIENT_ID",  
    ClientSecret = "YOUR_CLIENT_SECRET",  
});
于 2014-09-09T13:01:23.013 回答
0

确保启用了第 3 方 cookie。我发现,如果您在尝试使用您的应用程序“注册”用户时没有登录谷歌,它会重定向到登录页面,因为它会查找不存在的这个 cookie,但仍然设法做它需要与外部提供商。下次您尝试“注册”时,因为它已经完成了该过程的一部分,它不再需要查找外部 cookie,因此第二次成功。

于 2014-05-28T11:46:23.227 回答