Open ID Connect(OIDC)在 ASP.NET Core中的应用

我们在《ASP.NET Core项目实战的课程》第一章里面给identity server4做了一个全面的介绍和示例的练习 ,这篇文章是根据大家对OIDC遇到的一些常见问题整理得出。

本文将涉及到以下几个话题:

  • 什么是OpenId Connect (OIDC)
  • OIDC 对oAuth进行了哪些扩展?
  • Identity Server4提供的OIDC认证服务(服务端)
  • ASP.NET Core的权限体系中的OIDC认证框架(客户端)

什么是 OIDC

在了解OIDC之前,我们先看一个很常见的场景。假使我们现在有一个网站要集成微信或者新浪微博的登录,两者现在依然采用的是oAuth 2.0的协议来实现 。 关于微信和新浪微博的登录大家可以去看看它们的开发文档。

在我们的网站集成微博或者新浪微博的过程大致是分为五步:

  1. 准备工作:在微信/新浪微博开发平台注册一个应用,得到AppId和AppSecret
  2. 发起 oAauth2.0 中的 Authorization Code流程请求Code
  3. 根据Code再请求AccessToken(通常在我们应用的后端完成,用户不可见)
  4. 根据 AccessToken 访问微信/新浪微博的某一个API,来获取用户的信息
  5. 后置工作:根据用户信息来判断是否之前登录过?如果没有则创建一个用户并将这个用户作为当前用户登录(我们自己应用的登录逻辑,比如生成jwt),如果有了则用之前的用户登录。

中间第2到3的步骤为标准的oAuth2 授权码模式的流程,如果不理解的可以参考阮一峰所写的《理解oAuth2.0 》一文。我们主要来看第4和5步,对于第三方应用要集成微博登录这个场景来说最重要的是我希望能快速拿到用户的一些基本信息(免去用户再次输入的麻烦)然后根据这些信息来生成一个我自己的用户跟微博的用户Id绑定(为的是下次你使用微博登录的时候我还能把你再找出来)。

oAuth在这里麻烦的地方是我还需要再请求一次API去获取用户数据,注意这个API和登录流程是不相干的,其实是属于微博开放平台丛多API中的一个,包括微信开放平台也是这样来实现。这里有个问题是前面的 2和3是oAuth2的标准化流程,而第4步却不是,但是大家都这么干(它是一个大家都默许的标准)

于是大家干脆就建立了一套标准协议并进行了一些优化,它叫OIDC

OIDC 建立在oAuth2.0协议之上,允许客户端(Clients)通过一个授权服务(Authorization Server)来完成对用户认证的过程,并且可以得到用户的一些基本信息包含在JWT中。

OIDC对oAuth进行了哪些扩展?

在oAuth2.0授权码模式的帮助下,我们拿到了用户信息。

以上没有认证的过程,只是给我们的应用授权访问一个API的权限,我们通过这个API去获取当前用户的信息,这些都是通过oAuth2的授权码模式完成的。 我们来看看oAuth2 授权码模式的流程:

第一步,我们向authorize endpoint请求code的时候所传递的response_type表示授权类型,原来只有固定值code

GET /connect/authorize?response_type=code&client_id=postman&state=xyz&scope=api1
        &redirect_uri=http://localhost:5001/oauth2/callback

第二步,上面的请求执行完成之后会返回301跳转至我们传过去的redirect_uri并带上code

https://localhost:5001/oauth2/callback?code=835d584d4bc96d46ce49e27ebdbf272e40234d5f31097f63163f17da61fcd01c
&scope=api1
&state=111271607

第三步,用code换取access token

POST /connect/token?grant_type=authorization_code&code=835d584d4bc96d46ce49e27ebdbf272e40234d5f31097f63163f17da61fcd01c
&redirect_uri=http://localhost:5001/oauth2/callback
&client_id=postman
&client_secret=secret

通过这个POST我们就可以得到access_token

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjV",
    "expires_in": 3600,
    "token_type": "Bearer"
}

我们拿到access_token之后,再把access_token放到authorization头请求 api来获取用户的信息。在这里,这个api不是属于授权服务器提供的,而是属于资源服务器。

OIDC给oAuth2进行扩展之后就填补了这个空白,让我们可以授权它添加了以下两个内容:

  • response_type 添加IdToken
  • 添加userinfo endpoint,用idToken可以获取用户信息

OIDC对它进行了扩展,现在你有三个选择:code, id_token和 token,现在我们可以这样组合来使用。

"response_type" value

Flow

code

Authorization Code Flow

id_token

Implicit Flow

id_token token

Implicit Flow

code id_token

Hybrid Flow

code token

Hybrid Flow

code id_token token

Hybrid Flow

我们简单的来理解一下这三种模式:

  • Authorization Code Flow授权码模式:保留oAuth2下的授权模式不变response_type=code
  • Implicit Flow 隐式模式:在oAuth2下也有这个模式,主要用于客户端直接可以向授权服务器获取token,跳过中间获取code用code换accesstoken的这一步。在OIDC下,responsetype=token idtoken,也就是可以同时返回access_token和id_token。
  • Hybrid Flow 混合模式: 比较有典型的地方是从authorize endpoint 获取 code idtoken,这个时候id_token可以当成认证。而可以继续用code获取access_token去做授权,比隐式模式更安全。 再来详细看一下这三种模式的差异:

Property

Authorization Code Flow

Implicit Flow

Hybrid Flow

access token和id token都通过Authorization endpoint返回

no

yes

no

两个token都通过token end point 返回

yes

no

no

用户使用的端(浏览器或者手机)无法查看token

yes

no

no

Client can be authenticated

yes

no

yes

支持刷新token

yes

no

yes

不需要后端参与

no

yes

no

我们来看一下通过Hybird如何获取 code、id_token、_以及access_token,然后再用id_token向userinfo endpoint请求用户信息。

第一步:获取code,

  • response_type=code id_token
  • scope=api1 openid profile 其中openid即为用户的唯一识别号
GET /connect/authorize?response_type=code id_token&client_id=postman&state=xyz&scope=api1 openid profile
&nonce=7362CAEA-9CA5-4B43-9BA3-34D7C303EBA7
        &redirect_uri=http://localhost:5001/oauth2/callback

当我们使用OIDC的时候,我们请求里面多了一个nonce的参数,与state有异曲同工之妙。我们给它一个guid值即可。

第二步:我们的redirect_uri在接收的时候即可以拿到code 和 id_token

https://localhost:5001/oauth2/callback#
code=c5eaaaca8d4538f69f670a900d7a4fa1d1300b26ec67fba2f84129f0ab4ffa35
&id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjVjMzA5ZGIwYTE2OGEwOTgGtpbj0GVXNnkKhGdrzA
&scope=openid%20profile%20api1&state=111271607

第三步:用code换access_token(这一步与oAuth2中的授权码模式一致)

第四步:用access_token向userinfo endpoint获取用户资料

Get http://localhost:5000/connect/userinfo
Authorization Bearer access_token

返回的用户信息

{
    "name": "scott",
    "family_name": "liu",
    "sub": "5BE86359-073C-434B-AD2D-A3932222DABE"
}

以下是我们的流程示意图。

有人可能会注意到,在这里我们拿到的idtoken没有派上用场,我们的用户资料还是通过access_token从userinfo endpoint里拿的。这里有两个区别:

  1. userinfo endpoint是属于认证服务器实现的,并非资源服务器,有归属的区别
  2. id_token 是一个jwt,里面带有用户的唯一标识,我们在判断该用户已经存在的时候不需要再请求userinfo endpoint

下图是对id_token进行解析得到的信息:sub即subject_id(用户唯一标识 )

对jwt了解的同学知道它里面本身就可以存储用户的信息,那么id_token可以吗?答案当然是可以的,我们将在介绍完identity server4的集成之后最后来实现。

Identity Server4提供的OIDC认证服务

Identity Server4是asp.net core2.0实现的一套oAuth2 和OIDC框架,用它我们可以很快速的搭建一套自己的认证和授权服务。我们来看一下用它如何快速实现OIDC认证服务。

由于用户登录代码过多,完整代码可以加入ASP.NET Core QQ群 92436737获取。 此处仅展示配置核心代码。

过程

  • 新建asp.net core web应用程序
  • 添加identityserver4 nuget引用
  • 依赖注入初始化
services.AddIdentityServer()
                .AddDeveloperSigningCredential()
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddTestUsers(Config.GetTestUsers());
  • 中间件添加
app.UseIdentityServer();
  • 配置

在测试的时候我们新建一个Config.cs来放一些配置信息

api resources

public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("api1", "API Application"){
                    UserClaims = { "role", JwtClaimTypes.Role }
                }
            };
        }

identity resources

public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("api1", "API Application"){
                    UserClaims = { "role", JwtClaimTypes.Role }
                }
            };
        }

clients

我们要讲的关键信息在这里,client有一个AllowGrantTypes它是一个string的集合。我们要写进去的值就是我们在上一节讲三种模式: Code,Implict和Hybird。因为这三种模式决定了我们的response_type可以请求哪几个值,所以这个地方一定不能写错。

IdentityServer4.Models.GrantTypes这个枚举给我们提供了一些选项,实际上是把oAuth的4种和OIDC的3种进行了组保。

public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "postman",

                    AllowedGrantTypes = GrantTypes.Hybird,
                    RedirectUris = { "https://localhost:5001/oauth2/callback" },

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },

                     AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "api1"
                    },

                    AllowOfflineAccess=true,

                },
            };
        }

users

public static List<TestUser> GetTestUsers()
        {
            return new List<TestUser> {
            new TestUser {
                SubjectId = "5BE86359-073C-434B-AD2D-A3932222DABE",
                Username = "scott",
                Password = "password",
                Claims = new List<Claim> {

                    new Claim(JwtClaimTypes.Name, "scott"),
                    new Claim(JwtClaimTypes.FamilyName, "liu"),
                    new Claim(JwtClaimTypes.Email, "scott@scottbrady91.com"),
                    new Claim(JwtClaimTypes.Role, "user"),
                }
            }
            };
        }

ASP.NET Core的权限体系中的OIDC认证框架

在Microsoft.AspNetCore.All nuget引用中包含了Microsoft.AspNetCore.Authentication.OpenIdConnect即asp.net core OIDC的客户端。我们需要在依赖注入中添加以下配置:

services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
          .AddCookie("Cookies")
          .AddOpenIdConnect("oidc", options =>
          {
              options.SignInScheme = "Cookies";
              options.Authority = "http://localhost:5000";
              options.RequireHttpsMetadata = false;
              options.ClientId = "postman";
              options.ClientSecret = "secret";
              options.ResponseType = "code id_token";
              options.GetClaimsFromUserInfoEndpoint = true;
              options.Scope.Add("api1");
              options.Scope.Add("offline_access");
          });

Authority即我们的用identity server4搭建的认证授权服务器,而其中的GetClaimsFromUserInfoEndpoint则会在拿到id_token之后自动向userinfo endpoint请求用户信息并放到asp.net core的User Identity下。

我们上面讲过,可以不需要请求userinfo endpoint, 直接将用户信息放到id_token中。

这样我们就不需要再向userinfo endpoint发起请求,从id_token中即可以获取到用户的信息。而有了identity server4的帮助,完成这一步只需要一句简单的配置即可:

new Client
{
    ClientId = "postman",

    AlwaysIncludeUserClaimsInIdToken = true,
    AllowOfflineAccess=true,
}

 这样我们在拿到id_token之后,里即包含了我们的用户信息。

资料:

晓晨master的identity server4中文文档  http://www.cnblogs.com/stulzq/p/8119928.html 李念辉身份认证核心: https://www.cnblogs.com/linianhui/archive/2017/05/30/openid-connect-core.html OIDC协议: http://openid.net/specs/openid-connect-discovery-1_0.html Jesse腾飞的asp.net core项目实战第一章identity server4准备 http://video.jessetalk.cn/course/5

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GuZhenYin

ASP.NET Core之跨平台的实时性能监控(2.健康检查)

前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内容. 没看过上篇的,请移步:...

2546
来自专栏黑白安全

网站漏洞扫描工具 WAScan-Web Application Scanner

WAScan是一款开源工具,该工具采用的是基于黑盒的漏洞挖掘方法,这也就意味着研究人员无需对Web应用程序的源代码进行研究,它可以直接被当作成一种模糊测试工具来...

1313
来自专栏程序猿

Burp Suite第十八节: 使用Burp Suite测试Web Services服务

从这一节开始,我们进入了Burp的综合使用。通过一系列的使用场景的简单学习,逐渐熟悉Burp在渗透测试中,如何结合其他的工具,组合使用,提高工作...

3666
来自专栏Youngxj

记一次IP反查

2013
来自专栏最新技术

使用Eclipse MicroProfile(更新版)构建您的下一个微服务

本快速教程将向您展示如何使用最新版本的Eclipse MicroProfile API构建您的下一个微服务。这是一篇基于以前John D Ament 的文章的修...

2221
来自专栏Google Dart

Dart服务器端 mojito包 原

就像它的名字一样,Mojito主要是糖和其他成分的混合物。 Mojito故意在几个shelf包上非常薄,并专注于构建应用程序的整体体验。

881
来自专栏LIN_ZONE

ab 站点压力测试工具

apache自带的工具,所以只要安装上apache后,就能够使用 ab 工具进行网站压力测试

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

如何轻松爬取网页数据?

在实际工作中,难免会遇到从网页爬取数据信息的需求,如:从微软官网上爬取最新发布的系统版本。很明显这是个网页爬虫的工作。本文将根据网站特性进行分类介绍几种使用py...

2.7K1
来自专栏腾讯云CDN团队的专栏

使用腾讯云 CDN 、COS 以及万象优图实现HTTP/2样例

为了直观的看到使用HTTP/2协议所带来的优化效果,本文将介绍如何使用腾讯云CDN,COS以及万象优图来实现一个简单的demo。

1.3K1
来自专栏程序猿DD

Spring Boot 与 OAuth2

原文:Spring Boot and OAuth2 译者:nycgym 原文:http://www.spring4all.com/article/827 ? 本...

1.2K12

扫码关注云+社区