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

Posted

技术标签:

【中文标题】如何在没有实体框架提供程序的情况下在 .net 核心中实现谷歌登录【英文标题】:how to implement google login in .net core without an entityframework provider 【发布时间】:2018-12-06 14:51:44 【问题描述】:

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

在这段代码中

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

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

private SignInManager<AppUser> signInManager;

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

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

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

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

但这给了我这个错误:

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

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

.AddEntityFrameworkStores<ApplicationDbContext>()

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

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

【问题讨论】:

如果您不想使用实体框架,那么您必须自定义存储提供程序:docs.microsoft.com/en-us/aspnet/identity/overview/extensibility/… 如果您想使用实体框架但遇到一些错误(如您所述),您可以参考我的源码演示:bitbucket.org/tuanv2t/net-core-demo/src/master/NetCoreDemo/… 感谢您的回复。并不是我不想使用 EF(就像我想使用其他提供程序一样),而是我不需要数据库提供程序,因为我想使用命名(可在 json 文件中配置)用户。所以在配置文件中我有一个包含 5 个用户名的列表,并且通过谷歌登录我想允许他们登录。在这种情况下,我认为 EF 提供程序没有任何用处,这就是我考虑不使用它的原因。 我还查看了您的 bitbucket 代码,app.UseIdentity(); and app.UseGoogleAuthentication 代码行都被标记为过时。 【参考方案1】:

如果您只想使用 Google 登录,则不需要 SignInManagerUserManager 或 ASP.NET Core Identity 本身。为此,我们首先需要配置身份验证服务。这是相关的代码,我会在后面解释:

Startup.cs

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

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

AddCookie 的两次调用为Application(我们的Application 方案)和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);
    

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

    Login

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

    LoginCallback

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

针对以下 cmets 中的几个后续 cmets/问题:

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

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

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

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

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

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

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


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

【讨论】:

Use cookie authentication without ASP.NET Core Identity 也可能是有用的后续阅读。 感谢您的回复。如果您仍在使用 ASP.NET Core 2.2(针对完整框架),请确保使用 Microsoft.AspNetCore.Authentication.Google 的 2.2.2 版本。 @Leandro 我认为您所描述的内容不需要 ASP.NET Core Identity,但在 cmets 中很难回答这个问题。您可能仍需要使用 Cookie 来跟踪登录,例如Facebook 用户。 谢谢@Leandro。我真的很高兴你发现这很有帮助。我希望代码中的此语句将消除对Authorize 属性的显式需要:o.DefaultScheme = "Application";,但是。 @KirkLarkin,首先我必须说..你已经很好地解释了这一点!我对一个声明有点怀疑,它说:The Challenge scheme is used when a user is not signed in 如果我读错了,请纠正我,我认为我们可以使用挑战方案(调用ChallengeAsync()),即使给定的用户已经签名-in(例如,用另一个方案登录已经登录的用户)。

以上是关于如何在没有实体框架提供程序的情况下在 .net 核心中实现谷歌登录的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有实体框架的情况下使用 ASP.NET Identity 3.0 [关闭]

如何在没有 ef 的情况下在 MVC5 中使用参数进行搜索

如何使用 MS Access 作为 ADO.NET 实体框架的提供者?

如何让asp.net成员提供者和实体框架共享同一个数据库

如何在没有遍历框架的情况下在neo4j中进行深度优先遍历?

找不到具有不变名称“System.Data.SqlClient”的 ADO.NET 提供程序的实体框架提供程序。