如何在没有实体框架提供程序的情况下在 .net 核心中实现谷歌登录 [英] how to implement google login in .net core without an entityframework provider

查看:11
本文介绍了如何在没有实体框架提供程序的情况下在 .net 核心中实现谷歌登录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的 .net 核心站点实现 Google 登录.

I am implementing Google login for my .net core site.

在这段代码中

var properties = signInManager.ConfigureExternalAuthenticationProperties("Google", redirectUrl);      
return new ChallengeResult("Google", properties); 

我需要一个 signInManager 这是(通过代码示例):

I need a signInManager which is (by the code example) this:

private SignInManager<AppUser> signInManager;

我通过构造函数注入它,然后我得到这个错误:

I inject it via the constructor, and then I get this error:

在尝试激活AccountController"时无法解析Microsoft.AspNetCore.Identity.SignInManager1[AppUser]"类型的服务.

Unable to resolve service for type 'Microsoft.AspNetCore.Identity.SignInManager1[AppUser]' while attempting to activate 'AccountController'.

谷歌搜索得知我应该包含这个

Googling learnt that I should include this

services.AddIdentity<AppUser, IdentityRole>()
    .AddDefaultTokenProviders();`

但这给了我这个错误:

在尝试激活Microsoft.AspNetCore.Identity.AspNetUserManager1[AppUser]"时,无法解析Microsoft.AspNetCore.Identity.IUserStore1[AppUser]"类型的服务.

Unable to resolve service for type 'Microsoft.AspNetCore.Identity.IUserStore1[AppUser]' while attempting to activate 'Microsoft.AspNetCore.Identity.AspNetUserManager1[AppUser]'.

在那一刻,我得到了添加以下内容的建议:

And at that moment, I get the advice to add this:

.AddEntityFrameworkStores<ApplicationDbContext>()

但是我迷路了,因为为什么 SignInManager 需要一个 IUserStore,我应该添加一个UserStore 和一个 DBContext 和一个 EntityFramework 存储,我什么时候不会使用它(用于我的 Google 登录)?

But then I'm lost, because why does the SignInManager need a IUserStore, and should I add a UserStore and a DBContext and an EntityFramework store, when I will not be using that (for my Google login)?

所以问题是:我是否也可以在没有 Entityframework 商店的情况下进行 Google 登录?

So the question is: can I also do my Google login without the Entityframework store?

推荐答案

如果你只想用谷歌登录,就不需要SignInManagerUserManager 或 ASP.NET Core 标识本身.为此,我们首先需要配置身份验证服务.这是相关的代码,我会在后面解释:

If all you want to do is sign-in with Google, there's no need for SignInManager, UserManager or ASP.NET Core Identity itself. To achieve this, we first need to configure the Authentication services. Here's the relevant code for this, which I'll explain after:

Startup.cs

services
    .AddAuthentication(o =>
    {
        o.DefaultScheme = "Application";
        o.DefaultSignInScheme = "External";
    })
    .AddCookie("Application")
    .AddCookie("External")
    .AddGoogle(o =>
    {
        o.ClientId = ...;
        o.ClientSecret = ...;
    });

  • AddAuthentication 的调用配置了一个 DefaultScheme,它最终被用作 Application 方案和 挑战方案.Application 方案在尝试对用户进行身份验证时使用(他们是否已登录?).Challenge 方案在用户 登录但应用程序希望提供这样做的选项时使用.稍后我将讨论 DefaultSignInScheme.

    • The call to AddAuthentication configures a DefaultScheme, which ends up being used as both the Application scheme and the Challenge scheme. The Application scheme is used when attempting to authenticate the user (are they signed in?). The Challenge scheme is used when a user is not signed in but the application wants to provide the option to do so. I'll discuss the DefaultSignInScheme later.

      AddCookie 的两次调用为 Application(我们的 Application 方案)和 添加了基于 cookie 的身份验证方案外部(我们的 SignIn 方案).AddCookie 也可以采用第二个参数,允许配置例如对应cookie的生命周期等

      The two calls to AddCookie add cookie-based authentication schemes for both Application (our Application scheme) and External (our SignIn scheme). AddCookie can also take a second argument, that allows for configuration of e.g. the corresponding cookie's lifetime, etc.

      有了这个,质询过程会将用户重定向到 /Account/Login(默认情况下 - 这也可以通过 cookie 身份验证选项进行配置).这是一个处理挑战过程的控制器实现(再次,我将在后面解释):

      With this in place, the challenge process will redirect the user over to /Account/Login (by default - this can be configured via the cookie authentication options too). Here's a controller implementation that handles the challenge process (again, I'll explain after):

      AccountController.cs

      public class AccountController : Controller
      {
          public IActionResult Login(string returnUrl)
          {
              return new ChallengeResult(
                  GoogleDefaults.AuthenticationScheme,
                  new AuthenticationProperties
                  {
                      RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl })
                  });
          }
      
          public async Task<IActionResult> LoginCallback(string returnUrl)
          {
              var authenticateResult = await HttpContext.AuthenticateAsync("External");
      
              if (!authenticateResult.Succeeded)
                  return BadRequest(); // TODO: Handle this better.
      
              var claimsIdentity = new ClaimsIdentity("Application");
      
              claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
              claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Email));
      
              await HttpContext.SignInAsync(
                  "Application",
                  new ClaimsPrincipal(claimsIdentity));
      
              return LocalRedirect(returnUrl);
          }
      }
      

      让我们把它分解成两个动作:

      Let's break this down into the two actions:

      1. 登录

      为了到达 Login 操作,用户将受到挑战.当用户未使用 Application 方案登录但试图访问受 Authorize 属性(或类似属性)保护的页面时,会发生这种情况.根据您的要求,如果用户未登录,我们希望他们使用 Google 登录.为了实现这一点,我们发出了 挑战,这次是针对 Google 方案.我们使用一个配置了 Google 方案的 ChallengeResult 和一个 RedirectUrlGoogle 登录过程完成.如代码所示,我们返回:

      In order to arrive at the Login action, the user will have been challenged. This occurs when the user is not signed in using the Application scheme but is attempting to access a page protected by the Authorize attribute (or similar). Per your requirement, if the user is not signed in, we want to sign them in using Google. In order to achieve that, we issue a new challenge, this time for the Google scheme. We do so using a ChallengeResult that is configured with the Google scheme and a RedirectUrl, which is used for returning to our own application code once the Google sign-in process completes. As the code shows, we return to:

      LoginCallback

      这就是我们调用 AddAuthentication 中的 DefaultSignInScheme 变得相关的地方.作为 Google 登录过程完成的一部分,DefaultSignInScheme 用于设置一个 cookie,该 cookie 包含一个代表从 Google 返回的用户的 ClaimsPrincipal(这一切都在后面处理场景).LoginCallback 中的第一行代码获取了这个 ClaimsPrincipal 实例,该实例包含在一个首先检查是否成功的 AuthenticateResult 中.如果到目前为止一切顺利,我们最终会创建一个新的 ClaimsPrincipal,其中包含我们需要的任何声明(在本例中取自 Google),然后登录该 ClaimsPrincipal使用 Application 方案.最后,我们重定向到引起我们第一个挑战的页面.

      This is where the DefaultSignInScheme from our call to AddAuthentication becomes relevant. As part of the Google sign-in process completion, the DefaultSignInScheme is used for setting a cookie that contains a ClaimsPrincipal representing the user as returned from Google (this is all handled behind the scenes). The first line of code in LoginCallback grabs hold of this ClaimsPrincipal instance, which is wrapped up inside an AuthenticateResult that is first checked for success. If everything has been successful so far, we end up creating a new ClaimsPrincipal that contains whatever claims we need (taken from Google in this case) and then signing-in that ClaimsPrincipal using the Application scheme. Lastly, we redirect to the page that caused our first challenge.

      回应以下评论中的几个后续评论/问题:

      In response to a couple of follow-up comments/questions in the comments below:

      我是否可以断定 SignInManagerUserManager 仅在使用数据库身份验证时使用?

      Can I conclude that the SignInManager and UserManager are only used when using authentication with a database?

      在某些方面,是的,我认为这是公平的.尽管可以实现内存存储,但如果没有持久性,它并没有多大意义.但是,在您的情况下不使用这些类的真正原因仅仅是因为您不需要本地用户帐户来代表用户.这与坚持是相辅相成的,但值得做出区分.

      In some ways, yes, I think that's fair. Although it is possible to implement an in-memory store, it doesn't really make much sense with no persistence. However, the real reason not to use these classes in your situation is simply because you do not need a local user account for representing a user. That goes hand-in-hand with persistence, but it's worth making the distinction.

      我在书中读到的代码(用于设置我的谷歌登录)和我读过的所有其他答案有什么不同.

      And what very different code from what I read in the book (which I used for setting up my Google login) and all the other answers I've read.

      文档和书籍涵盖了最常见的用例,您确实希望存储可以链接到外部帐户(例如 Google 等)的本地用户. 如果您查看 SignInManager 源代码,您会发现它实际上只是位于我上面显示的那种代码之上(例如 这里这里).其他代码可以在默认 UI 中找到(例如 这里)和 AddIdentity.

      The documentation and the books cover the most common use-case, whereby you do want to store local users that can be linked to external accounts such as Google, etc. If you look at the SignInManager source, you'll see that it's really just sitting on top of the kind of code I've shown above (e.g. here and here). Other code can be found in the Default UI (e.g. here) and in AddIdentity.

      我假设 LoginCallback 被 Google 调用.HttpContext.AuthenticateAsync 是否知道如何检查 Google 发送给我的数据?而且因为它的名字太笼统了,看起来它知道如何为所有外部提供者做到这一点?

      I assume the LoginCallback gets called by Google. Does the HttpContext.AuthenticateAsync know how to check the data Google sends me? And as it's name is so generic, it looks like it knows how to do that for all external providers?

      此处对 AuthenticateAsync 的调用不了解关于 Google 的任何内容 - 特定于 Google 的处理由对 AddGoogle 的调用配置ConfigureServices 中的 AddAuthentication.在重定向到 Google 进行登录后,我们实际上回到了应用程序中的 /signin-google.同样,这要归功于对 AddGoogle 的调用,但该代码实际上只是在 External 方案中发出一个 cookie,该方案存储从 Google 返回的声明,然后重定向到我们配置的 LoginCallback 端点.如果添加对 AddFacebook 的调用,/sigin-facebook 端点将被配置为执行类似操作.对 AuthenticateAsync 的调用实际上只是从由例如创建的 cookie 中补充一个 ClaimsPrincipal./signin-google 端点,以便检索声明.

      The call to AuthenticateAsync here doesn't know anything about Google - the Google-specific handling is configured by the call to AddGoogle off of AddAuthentication in ConfigureServices. After redirecting to Google for sign-in, we actually come back to /signin-google in our application. Again, this is handled thanks to the call to AddGoogle, but that code is really just issuing a cookie in the External scheme that stores the claims that came back from Google and then redirecting to our LoginCallback endpoint that we configured. If you add a call to AddFacebook, a /sigin-facebook endpoint will be configured to do something similar. The call to AuthenticateAsync is really just rehydrating a ClaimsPrincipal from the cookie that was created by e.g. the /signin-google endpoint, in order to retrieve the claims.

      另外值得注意的是,Google/Facebook 登录过程基于 OAuth 2 协议,所以它本身就是通用的.如果您需要的不仅仅是 Google 的支持,您只需针对所需方案发出挑战,而不是像我在示例中所做的那样将其硬编码给 Google.还可以向质询添加其他属性,以便能够确定在到达 LoginCallback 端点时使用了哪个提供程序.

      It's also worth noting that the Google/Facebook sign-in process is based on the OAuth 2 protocol, so it's kind of generic in itself. If you were to need support for more than just Google, you would just issue the challenge against the required scheme rather than hardcoding it to Google as I've done in the example. It's also possible to add additional properties to the challenge in order to be able to determine which provider was used when your LoginCallback endpoint is reached.

      我创建了一个 GitHub 存储库,其中包含我构建的完整示例,以便编写此答案这里.

      I've created a GitHub repository that contains a complete example that I built in order to write this answer here.

      这篇关于如何在没有实体框架提供程序的情况下在 .net 核心中实现谷歌登录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆