IdentityServer Topics(5)- 使用第三方登录

ASP.NET Core有一个灵活的方式来处理外部认证。 这包括几个步骤。

如果您使用的是ASP.NET Identity,则许多底层技术细节对您而言都是隐藏的。 建议您还阅读Microsoft文档并查看ASP.NET Identity快速入门源码。

添加外部认证处理程序

与外部提供者交互所需的协议实现被封装在一个认证处理程序中。 一些提供者使用专有协议(例如Facebook等社交提供者),一些使用标准协议, OpenID Connect,WS-Federation或SAML2p。

请参阅此快速入门以了解添加外部认证并对其进行配置的分步说明。

cookies的作用

外部认证处理程序上的一个选项称为SignInScheme,例如:

services.AddAuthentication()
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = "scheme of cookie handler to use";

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

登录方案指定将暂时存储外部认证的结果的cookie处理程序的名称,例如 由外部提供商发送的身份单元。 这是必要的,因为在完成外部认证过程之前,通常会有几个重定向。

鉴于这是一种常见的做法,IdentityServer专门为此外部提供程序工作流程注册一个Cookie处理程序。 该方案通过IdentityServerConstants.ExternalCookieAuthenticationScheme常量表示。 如果您要使用我们的外部cookie处理程序,那么对于上面的SignInScheme,您将分配的值为IdentityServerConstants.ExternalCookieAuthenticationScheme常量:

services.AddAuthentication()
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

您也可以注册您自己的自定义Cookie处理程序,如下所示:

services.AddAuthentication()
    .AddCookie("YourCustomScheme")
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = "YourCustomScheme";

        options.ClientId = "...";
        options.ClientSecret = "...";
    })

对于特定的场景,您还可以将外部Cookie机制短路,并将外部用户直接转发到主要Cookie处理程序。 这通常涉及在外部处理程序上处理事件,以确保从外部身份源执行正确的声明转换。

触发认证处理程序

您可以通过HttpContext上的ChallengeAsync扩展方法(或使用MVC ChallengeResult)调用外部认证处理程序。

您通常希望将某些设置项传递给质询操作,例如 您的回调页面的路径和提供登记的名称,例如:

var callbackUrl = Url.Action("ExternalLoginCallback");

var props = new AuthenticationProperties
{
    RedirectUri = callbackUrl,
    Items =
    {
        { "scheme", provider },
        { "returnUrl", returnUrl }
    }
};

return Challenge(provider, props);

回调处理程序和用户签名

在回调页面上,您的典型任务是:

  • 检查由外部提供商返回的身份。
  • 做一个决定你想如何处理这个用户。 如果这是一个新用户或一个返回用户,这可能会有所不同。
  • 新用户在允许之前可能需要额外的步骤和UI。
  • 可能会创建一个链接到外部提供程序的新的内部用户帐户。
  • 存储您要保留的外部声明。
  • 删除临时cookie
  • 登录用户

检查外部身份:

// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var externalUser = result.Principal;
if (externalUser == null)
{
    throw new Exception("External authentication error");
}

// retrieve claims of the external user
var claims = externalUser.Claims.ToList();

// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
    userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
}
if (userIdClaim == null)
{
    throw new Exception("Unknown userid");
}

var externalUserId = userIdClaim.Value;
var externalProvider = userIdClaim.Issuer;

// use externalProvider and externalUserId to find your user, or provision a new user

清理和登录:

// issue authentication cookie for user
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray());

// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);

// validate return URL and redirect back to authorization endpoint or a local page
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
{
    return Redirect(returnUrl);
}

return Redirect("~/");

状态,URL长度和ISecureDataFormat

当重定向到外部提供商登录时,来自客户端应用程序的状态必须频繁进行往返。 这意味着状态在离开客户端之前被捕获并保存直到用户返回到客户端应用程序。 许多协议(包括OpenID Connect)都允许将某种状态作为参数传递给请求,身份提供者将在响应中返回该状态。 ASP.NET Core提供的OpenID Connect身份验证处理程序利用了该协议的这一功能,这就是它如何实现上述的returnUrl功能。

在请求参数中存储状态的问题是请求URL可能会变得太大(超过2000个字符的公共限制)。 OpenID Connect身份验证处理程序的确提供了一个可扩展点,用于将状态存储在服务器中,而不是在请求URL中。 您可以通过实现ISecureDataFormat 并在OpenIdConnectOptions上配置它来实现这一点。

幸运的是,IdentityServer为您提供了一个实现,由在DI容器中注册的IDistributedCache实现(例如,独立的MemoryDistributedCache)支持。 要使用IdentityServer提供的安全数据格式实现,只需在配置DI时在IServiceCollection上调用AddOidcStateDataFormatterCache扩展方法即可。 如果没有参数传递,则所有配置的OpenID Connect处理程序将使用IdentityServer提供的安全数据格式实现:

public void ConfigureServices(IServiceCollection services)
{
    // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
    services.AddOidcStateDataFormatterCache();

    services.AddAuthentication()
        .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
        {
            // ...
        })
        .AddOpenIdConnect("aad", "Azure AD", options =>
        {
            // ...
        })
        .AddOpenIdConnect("adfs", "ADFS", options =>
        {
            // ...
        });
}

如果只配置特定方案,则将这些方案作为参数传递:

public void ConfigureServices(IServiceCollection services)
{
    // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
    services.AddOidcStateDataFormatterCache("aad", "demoidsrv");

    services.AddAuthentication()
        .AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
        {
            // ...
        })
        .AddOpenIdConnect("aad", "Azure AD", options =>
        {
            // ...
        })
        .AddOpenIdConnect("adfs", "ADFS", options =>
        {
            // ...
        });
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

3 条评论
登录 后参与评论

相关文章

来自专栏腾讯NEXT学位

小程序iOS客户端框架——控件事件逻辑框架与控件原生化(上)

? 小程序自发布以来,为开发者和用户提供了一种轻量级的App。作为一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打...

641
来自专栏魏琼东

分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载

一、分布式消息总线      在很多MIS项目之中都有这样的需求,需要一个及时、高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经...

3437
来自专栏友弟技术工作室

Django 优秀资源大全项目资源非 Python 包工具贡献

版权: https://github.com/haiiiiiyun/awesome-django-cn Awesome Django 介绍 Awesome-Dj...

7999
来自专栏Android机器圈

Servlet与Jsp的结合使用实现信息管理系统一

PS:1:先介绍一下什么是Servlet? Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java...

2959
来自专栏Python爬虫与算法进阶

Scrapy源码(1)——爬虫流程概览

前言 使用 Scrapy 已经有一段时间了,觉得自己有必要对源码好好的学习下了,所以写下记录,希望能加深自己的理解。 Scrapy | A Fast and P...

3214
来自专栏SeanCheney的专栏

爬虫框架整理汇总

2316
来自专栏腾讯移动品质中心TMQ的专栏

H5前端性能测试快速入门

前言 说到H5测试,对于做WEB测试的同学来说再熟悉不过了,它包括页H5功能测试,前端性能测试,浏览器兼容性能测试,以及服务端性能测试。那本文谈到...

2156
来自专栏张善友的专栏

使用 ServiceStack 构建跨平台 Web 服务

本文主要来自MSDN杂志《Building Cross-Platform Web Services with ServiceStack》,Windows Com...

1995
来自专栏Danny的专栏

Web开发——服务器端应用技术简单比较

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

711
来自专栏张善友的专栏

ASP.NET 调味品:AJAX

Karl Seguin 适用于: AJAX(异步 JavaScript 和 XML) Microsoft AJAX.NET Microsoft ASP.NET ...

1935

扫码关注云+社区