没有 ASP.NET 身份的 .NET Core 外部身份验证

Posted

技术标签:

【中文标题】没有 ASP.NET 身份的 .NET Core 外部身份验证【英文标题】:.NET Core External Authentication without ASP.NET Identity 【发布时间】:2018-06-15 16:09:59 【问题描述】:

我使用自己的 JWT 令牌身份验证,而不是默认模板免费提供的 asp.net 身份。我到处寻找一些关于如何在没有 asp.net 身份的情况下实现外部身份验证的文档/指南,但所有文章都是针对 asp.net 身份验证的。

我已设法将用户重定向到 google 登录页面(使用 ChallengeResult),但是当提供商重定向回应用程序时,我失败了。

我已删除:Startup.cs 中的 app.UseAuthentication();,(禁用身份验证),然后我能够访问回调函数,但随后我不知道如何在不使用登录管理器的情况下从响应中检索数据..

启动

public class Startup

    public Startup(IHostingEnvironment env)
    
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    

    public IConfigurationRoot Configuration  get; 

    public void ConfigureServices(IServiceCollection services)
    
        var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Authentication:Secret"]));

        var tokenValidationParameters = new TokenValidationParameters
        
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
            ValidateIssuer = true,
            ValidIssuer = Configuration["Urls:Base"],
            ValidateAudience = true,
            ValidAudience = Configuration["Urls:Base"],
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        ;

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
        
            o.TokenValidationParameters = tokenValidationParameters;
        
        ).AddGoogle(googleOptions =>
        
            googleOptions.ClientId = "x";//Configuration["Authentication:Google:ClientId"];
            googleOptions.ClientSecret = "x";//Configuration["Authentication:Google:ClientSecret"];
            googleOptions.CallbackPath = "/api/authentication/externalauthentication/externallogincallback";
        );

        services.Configure<RequestLocalizationOptions>(
            opts =>
            
                var supportedCultures = new List<CultureInfo>
                
                        new CultureInfo("en"),
                        new CultureInfo("sv")
                ;

                opts.DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en");
                opts.SupportedCultures = supportedCultures;
                opts.SupportedUICultures = supportedCultures;
            );

        services.AddMvc(config =>
        
            var policy = new AuthorizationPolicyBuilder()
                             .RequireAuthenticatedUser()
                             .Build();

            config.Filters.Add(new AuthorizeFilter(policy));
        );

        services.RegisterAppSettings(Configuration);

        services.AddOptions();

        services.InjectServices();
    

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    
        app.UseAuthentication();

        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();

            EndpointsAppSettings endpointAppSettings = new EndpointsAppSettings();
            Configuration.GetSection("Endpoints").Bind(endpointAppSettings);

            app.UseCors(builder =>
            
                builder.WithOrigins(endpointAppSettings.Aurelia)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
            );
        

        var logService = app.ApplicationServices.GetService<ILogService>();

        loggerFactory.AddProvider(new LogProvider(logService));

        app.UseRequestLocalization(app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value);

        app.UseMvc();

        app.UseDefaultFiles();

        app.UseStaticFiles();
    

控制器

[Route("api/authentication/[controller]")]
public class ExternalAuthenticationController : Controller

    [AllowAnonymous]
    [HttpPost(nameof(ExternalLogin))]
    public IActionResult ExternalLogin(ExternalLoginModel model)
    
        if (model == null || !ModelState.IsValid)
        
            return null;
        

        var properties = new AuthenticationProperties  RedirectUri = "http://localhost:3000/#/administration/organisations" ;

        return Challenge(properties, model.Provider);
    

    [AllowAnonymous]
    [HttpGet(nameof(ExternalLoginCallback))]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    
        if (remoteError != null)
        
            return null;
        

        //Help me retrieve information here!

        return null;
    

ExternalLoginCallback 的堆栈跟踪

信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 GET http://localhost:5000/api/authentication/externalauthentication/externallogincallback?state=CfDJ8CyKJfDTf--HIDDEN DATA--52462e4156a..5cde&prompt=none 失败:Microsoft.AspNetCore.Server.Kestrel[13] 连接 ID“0HLAKEGSHERH7”,请求 ID“0HLAKEGSHERH7:00000002”:应用程序引发了未处理的异常。 System.InvalidOperationException:没有配置 IAuthenticationSignInHandler 来处理方案的登录:Bearer 在 Microsoft.AspNetCore.Authentication.AuthenticationService.d__13.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler d__12.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.d__6.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext() --- 从先前抛出异常的位置结束堆栈跟踪 --- 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame`1.d__2.MoveNext()

    为什么我得到:没有配置 IAuthenticationSignInHandler 来处理方案的登录:Bearer,如何解决? 如何在 ExternalLoginCallback 操作中检索用户信息?使用默认的 mvc 模板很简单: var info = await _signInManager.GetExternalLoginInfoAsync();但我没有使用登录管理器。 我根本没有找到任何关于此的文档,当然我不能成为唯一一个不使用内置 jumbo dumbo asp.net 身份而需要外部身份验证的人吗?如果您是比我更好的谷歌用户,请为我指明正确的方向!

【问题讨论】:

您可能想要备份并解释您要完成的工作。您是否查看过组织身份验证模板?它不使用身份,它使用一个 OpenIdConnect 提供程序。任何其他远程提供商都可以在这里替代(例如 Google、Facebook、Twitter、Microsoft 等)。 使用远程提供程序进行身份验证后,您想做什么?发行不记名令牌? 将用户重定向到 google => 登录 => 重定向回回调操作 => 获取用户信息,例如电子邮件等 => 创建新帐户并发出 jwt 令牌,或者如果用户已经存在但不存在与 google 关联 = 验证密码并将 google 帐户与用户帐户关联 查看existing Auth Middlewares,例如 OAuth 或 OpenID 用于与外部身份验证的通用连接,或 Twitter、Facebook、Google 和 MicrosoftAcount 以获得更具体的连接。这些用于对您进行身份验证。要使用 WebApi 中的 JWT,请使用 JwtAuthentcation 中间件 想通了 :) 如果您对我的回答有什么要补充的,请发表评论,谢谢 【参考方案1】:

解决:

没有配置 IAuthenticationSignInHandler 来处理登录 方案:承载

我必须添加一个临时存储外部身份验证结果的 cookie 处理程序,例如由外部提供者发送的声明。这是必要的,因为在您完成外部身份验证过程之前通常会涉及到几个重定向。

启动

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>

    o.TokenValidationParameters = tokenValidationParameters;
)
.AddCookie()
.AddGoogle(googleOptions =>

    googleOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    googleOptions.ClientId = "x";//Configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = "x";//Configuration["Authentication:Google:ClientSecret"];
    //googleOptions.CallbackPath = "/api/authentication/externalauthentication/signin-google";
);

这里的重要部分是 CookieAuthenticationDefaults.AuthenticationScheme。这是一个存储“Cookies”的字符串常量。虽然我们可以在代码中直接使用字符串“Cookies”,但使用预设常量会更安全。这是默认情况下赋予AddCookies 函数的身份验证方案名称。它可以帮助您参考 cookie 身份验证。

现在是时候从回调操作中的外部身份验证提供的声明中检索用户信息了。

控制器

[AllowAnonymous]
[HttpPost(nameof(ExternalLogin))]
public IActionResult ExternalLogin(ExternalLoginModel model)

    if (model == null || !ModelState.IsValid)
    
        return null;
    

    var properties = new AuthenticationProperties  RedirectUri = _authenticationAppSettings.External.RedirectUri ;

    return Challenge(properties, model.Provider);


[AllowAnonymous]
[HttpGet(nameof(ExternalLoginCallback))]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)

    //Here we can retrieve the claims
    var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

    return null;

瞧!我们现在可以处理一些用户信息!

有用的链接

http://docs.identityserver.io/en/latest/topics/signin_external_providers.html

【讨论】:

看起来不错。你记得把app.UseAuthentication(); 放回去吗? 关于您的“YourCustomScheme”,您不必使用它。默认情况下,它设置为“Cookies”。因此,您可以使用googleOptions.SignInScheme = "Cookies" 并将AddCookie() 留空

以上是关于没有 ASP.NET 身份的 .NET Core 外部身份验证的主要内容,如果未能解决你的问题,请参考以下文章

ASP .NET Core 身份登录管理器

ASP.NET Core 中的 Jwt 令牌身份验证

ASP.NET Core Web API 模板中没有个人用户帐户身份验证选项

使用 ASP.NET Core 进行 Ws-Federation 身份验证

asp.Net core 2.2中的多种身份验证方法

ASP.Net Core - 登录后如何等待用户身份立即可用?