如何在没有entityframework提供程序的情况下在.net核心中实现谷歌登录

Mic*_*hel 4 c# authentication asp.net-core-mvc .net-core asp.net-core

我正在为我的.net核心网站实施Google登录.

在这段代码中

var properties = signInManager.ConfigureExternalAuthenticationProperties("Google", redirectUrl);      
return new ChallengeResult("Google", properties); 
Run Code Online (Sandbox Code Playgroud)

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

private SignInManager<AppUser> signInManager;
Run Code Online (Sandbox Code Playgroud)

我通过构造函数注入它,然后我收到此错误:

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

谷歌搜索了我应该包括这个

services.AddIdentity<AppUser, IdentityRole>()
    .AddDefaultTokenProviders();`
Run Code Online (Sandbox Code Playgroud)

但这给了我这个错误:

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

在那一刻,我得到了建议:

.AddEntityFrameworkStores<ApplicationDbContext>()
Run Code Online (Sandbox Code Playgroud)

但后来我迷路了,因为为什么SignInManager需要a IUserStore,我应该添加一个 UserStore和一个DBContext和一个EntityFramework商店,当我不会使用它(我的谷歌登录)?

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

Kir*_*kin 19

如果你想要做的是登录在与谷歌,有没有需要SignInManager,UserManager或ASP.NET核心身份本身.为此,我们首先需要配置身份验证服务.这是相关的代码,我将在后面解释:

Startup.cs

services
    .AddAuthentication(o =>
    {
        o.DefaultScheme = "Application";
        o.DefaultSignInScheme = "External";
    })
    .AddCookie("Application")
    .AddCookie("External")
    .AddGoogle(o =>
    {
        o.ClientId = ...;
        o.ClientSecret = ...;
    });
Run Code Online (Sandbox Code Playgroud)
  • AddAuthentication配置a 的调用DefaultScheme最终被用作Application方案和Challenge方案.该应用程序尝试对用户进行身份验证方案时使用(他们是在签署?).该挑战当用户计划使用登录,但应用程序要提供这样做的选择.我稍后会讨论DefaultSignInScheme.

  • 这两个调用AddCookieApplication(我们的应用程序方案)和External(我们的SignIn方案)添加基于cookie的身份验证方案.AddCookie也可以采用第二个参数,允许配置例如相应的cookie的生命周期等.

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  1. Login

    为了达到该Login动作,用户将受到挑战.当用户未使用该Application方案登录但尝试访问受该Authorize属性(或类似)保护的页面时,会发生这种情况.根据您的要求,如果用户未登录,我们希望使用Google对其进行签名.为了实现这一目标,我们向该计划发出了新的挑战Google.我们使用ChallengeResult配置了Googlescheme和a的RedirectUrl方法来完成此操作,用于在Google登录过程完成后返回我们自己的应用程序代码.如代码所示,我们返回到:

  2. LoginCallback

    这就是DefaultSignInScheme我们呼吁AddAuthentication变得相关的地方.作为Google登录流程完成的一部分,该DefaultSignInScheme功能用于设置一个Cookie,其中包含ClaimsPrincipal代表Google返回的用户(这一切都在幕后处理).LoginCallback抓取此ClaimsPrincipal实例的第一行代码包含在AuthenticateResult首先检查成功的内部.如果到目前为止一切都取得了成功,我们最终会创建一个ClaimsPrincipal包含我们需要的任何声明的新内容(在这种情况下取​​自Google),然后ClaimsPrincipal使用该Application方案登录.最后,我们重定向到导致我们第一次挑战的页面.


我已经创建了一个GitHub存储库,其中包含我为了在此处编写此答案而构建的完整示例.


针对以下评论中的一些后续评论/问题:

我是否可以得出这样的结论SignInManager,并UserManager使用身份验证数据库时,只使用?

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

和我在书中看到的(我用于设置我的Google登录信息)和我读过的所有其他答案的代码完全不同.

文档和书籍涵盖最常见的用例,因此你想存储可以是本地用户链接到外部帐户,如谷歌,等等.如果你看看SignInManager源代码,你会发现它真的只是坐在在我上面显示的代码类型之上(例如这里这里).其他代码可以在默认UI(例如此处)和中找到AddIdentity.

我假设Google会调用LoginCallback.HttpContext.AuthenticateAsync是否知道如何检查Google发送给我的数据?因为它的名字是如此通用,看起来它知道如何为所有外部提供商做到这一点?

要将呼叫AuthenticateAsync这里不知道任何关于谷歌-谷歌与具体处理由调用配置为AddGoogle关闭的AddAuthenticationConfigureServices.在重定向到Google进行登录后,我们实际上会回到/signin-google我们的应用程序中.再次,由于调用,这是处理AddGoogle,但该代码实际上只是在External方案中发布一个cookie ,存储从谷歌返回的声明,然后重定向到LoginCallback我们配置的端点.如果添加呼叫AddFacebook,/sigin-facebook端点将配置为执行类似操作.调用AuthenticateAsync实际上只是ClaimsPrincipal从例如/signin-google端点创建的cookie中重新水化,以便检索声明.

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

  • [在没有 ASP.NET Core Identity 的情况下使用 cookie 身份验证](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.2) 可能是有用的后续阅读也。 (2认同)