NetCore OpenIdConnect验证为什么要设置Authority?

news2025/1/17 8:54:09

 在使用Identity Server作Identity Provider的时候,我们在NetCore的ConfigureServices((IServiceCollection services))方法中,常需要指定options的Authority,如下代码所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

public void ConfigureServices(IServiceCollection services)

       {

           services.AddControllersWithViews();

          

           //

           #region MVC client

           //关闭了 JWT 身份信息类型映射

           //这样就允许 well-known 身份信息(比如,“sub” 和 “idp”)无干扰地流过。

           //这个身份信息类型映射的“清理”必须在调用 AddAuthentication()之前完成

           JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

           services.AddAuthentication(options =>

           {

               options.DefaultScheme = "Cookies";

               options.DefaultChallengeScheme = "oidc";

           })

              .AddCookie("Cookies")

             .AddOpenIdConnect("oidc", options =>

             {

                

                 options.Authority = "http://localhost:5001";

                 options.RequireHttpsMetadata = false;

                 options.ClientId = "code_client";

                 //Client secret

                 options.ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A".ToString();

                 //code方式

                 options.ResponseType = "code";

                 //Scope可以理解为申请何种操作范围

                 options.Scope.Add("code_scope1"); //添加授权资源

   

                 options.SaveTokens = true;

                 options.GetClaimsFromUserInfoEndpoint = true;

                

             });

           #endregion

           services.ConfigureNonBreakingSameSiteCookies();

       }

  其中options.Authority = "http://localhost:5001"需要设置,如果把它注释掉,则会报如下的错误,从这个异常看得出来,应该是Authority没有设置:

 

   那么为什么我们一定要设置.Authority 呢?我们一步一步来看看:

  1.在调用services.AddOpenIdConnect方法时,实质调用的是OpenIdConnectExtensions静态类的AddOpenIdConnect静态方法,如下所示:

1

2

3

4

5

public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action<OpenIdConnectOptions> configureOptions)

       {

           builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>());

           return builder.AddRemoteScheme<OpenIdConnectOptions, OpenIdConnectHandler>(authenticationScheme, displayName, configureOptions);

       }

 2.从上面的代码看得出,实际上调用的是AuthenticationBuilder的AddRemoteScheme扩展方法,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

/// <summary>

        /// Adds a <see cref="AuthenticationScheme"/> which can be used by <see cref="IAuthenticationService"/>.

        /// </summary>

        /// <typeparam name="TOptions">The <see cref="AuthenticationSchemeOptions"/> type to configure the handler."/>.</typeparam>

        /// <typeparam name="THandler">The <see cref="AuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>

        /// <param name="authenticationScheme">The name of this scheme.</param>

        /// <param name="displayName">The display name of this scheme.</param>

        /// <param name="configureOptions">Used to configure the scheme options.</param>

        /// <returns>The builder.</returns>

        public virtual AuthenticationBuilder AddScheme<TOptions, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]THandler>(string authenticationScheme, string? displayName, Action<TOptions>? configureOptions)

            where TOptions : AuthenticationSchemeOptions, new()

            where THandler : AuthenticationHandler<TOptions>

            => AddSchemeHelper<TOptions, THandler>(authenticationScheme, displayName, configureOptions);        private AuthenticationBuilder AddSchemeHelper<TOptions, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]THandler>(string authenticationScheme, string? displayName, Action<TOptions>? configureOptions)

            where TOptions : AuthenticationSchemeOptions, new()

            where THandler : class, IAuthenticationHandler

        {

            Services.Configure<AuthenticationOptions>(o =>

            {

                //注册Scheme对应的HandlerType

                o.AddScheme(authenticationScheme, scheme => {

                    scheme.HandlerType = typeof(THandler);

                    scheme.DisplayName = displayName;

                });

            });

            if (configureOptions != null)

            {

                Services.Configure(authenticationScheme, configureOptions);

            }

            //Options验证

            Services.AddOptions<TOptions>(authenticationScheme).Validate(o => {

                o.Validate(authenticationScheme);

                return true;

            });

            Services.AddTransient<THandler>();

            return this;

        }

  

  看得出来,实际上调用的是AddSchemeHelper方法,在这个方法里,有一个很重要的Options验证:

1

2

3

4

Services.AddOptions<TOptions>(authenticationScheme).Validate(o => {

               o.Validate(authenticationScheme);

               return true;

           });在这里需要对AuthenticationSchemeOptions进行验证

   3.上一步的Options验证中实际上调用的是OpenIdConnectOptions类的Validate方法,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

/// <summary>

       /// Check that the options are valid.  Should throw an exception if things are not ok.

       /// </summary>

       public override void Validate()

       {

           base.Validate();

           if (MaxAge.HasValue && MaxAge.Value < TimeSpan.Zero)

           {

               throw new ArgumentOutOfRangeException(nameof(MaxAge), MaxAge.Value, "The value must not be a negative TimeSpan.");

           }

           if (string.IsNullOrEmpty(ClientId))

           {

               throw new ArgumentException("Options.ClientId must be provided", nameof(ClientId));

           }

           if (!CallbackPath.HasValue)

           {

               throw new ArgumentException("Options.CallbackPath must be provided.", nameof(CallbackPath));

           }

           //如果Authority没有设置,则报下面这个异常

           if (ConfigurationManager == null)

           {

               throw new InvalidOperationException($"Provide {nameof(Authority)}, {nameof(MetadataAddress)}, "

               + $"{nameof(Configuration)}, or {nameof(ConfigurationManager)} to {nameof(OpenIdConnectOptions)}");

           }

       }

  从这里看得出来,如果ConfigurationManager为空,则就会报前面中的异常了,所以异常就是这么来的。

4.那么为什么ConfigurationManager为空呢?我们回顾OpenIdConnectExtensions的AddOpenIdConnect方法:

1

2

3

4

5

public static AuthenticationBuilder AddOpenIdConnect(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action<OpenIdConnectOptions> configureOptions)

     {

         builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>());

         return builder.AddRemoteScheme<OpenIdConnectOptions, OpenIdConnectHandler>(authenticationScheme, displayName, configureOptions);

     }  

看得出AuthenticationBuilder的Services添加了一个名为OpenIdConnectPostConfigureOptions的单例服务: builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureOptions>());

继续看下OpenIdConnectPostConfigureOptions的源码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

/// <summary>

  /// Invoked to post configure a TOptions instance.

  /// </summary>

  /// <param name="name">The name of the options instance being configured.</param>

  /// <param name="options">The options instance to configure.</param>

  public void PostConfigure(string name, OpenIdConnectOptions options)

  {

      options.DataProtectionProvider = options.DataProtectionProvider ?? _dp;

      if (string.IsNullOrEmpty(options.SignOutScheme))

      {

          options.SignOutScheme = options.SignInScheme;

      }

      if (options.StateDataFormat == null)

      {

          var dataProtector = options.DataProtectionProvider.CreateProtector(

              typeof(OpenIdConnectHandler).FullName!, name, "v1");

          options.StateDataFormat = new PropertiesDataFormat(dataProtector);

      }

      if (options.StringDataFormat == null)

      {

          var dataProtector = options.DataProtectionProvider.CreateProtector(

              typeof(OpenIdConnectHandler).FullName!,

              typeof(string).FullName!,

              name,

              "v1");

          options.StringDataFormat = new SecureDataFormat<string>(new StringSerializer(), dataProtector);

      }

      if (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(options.ClientId))

      {

          options.TokenValidationParameters.ValidAudience = options.ClientId;

      }

      if (options.Backchannel == null)

      {

          options.Backchannel = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler());

          options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core OpenIdConnect handler");

          options.Backchannel.Timeout = options.BackchannelTimeout;

          options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB

      }

      if (options.ConfigurationManager == null)

      {

          if (options.Configuration != null)

          {

              options.ConfigurationManager = new StaticConfigurationManager<OpenIdConnectConfiguration>(options.Configuration);

          }

          else if (!(string.IsNullOrEmpty(options.MetadataAddress) && string.IsNullOrEmpty(options.Authority)))

          {

              if (string.IsNullOrEmpty(options.MetadataAddress) && !string.IsNullOrEmpty(options.Authority))

              {

                  options.MetadataAddress = options.Authority;

                  if (!options.MetadataAddress.EndsWith("/", StringComparison.Ordinal))

                  {

                      options.MetadataAddress += "/";

                  }

                  options.MetadataAddress += ".well-known/openid-configuration";

              }

              if (options.RequireHttpsMetadata && !(options.MetadataAddress?.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ?? false))

              {

                  throw new InvalidOperationException("The MetadataAddress or Authority must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false.");

              }

              options.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(options.MetadataAddress, new OpenIdConnectConfigurationRetriever(),

                  new HttpDocumentRetriever(options.Backchannel) { RequireHttps = options.RequireHttpsMetadata })

              {

                  RefreshInterval = options.RefreshInterval,

                  AutomaticRefreshInterval = options.AutomaticRefreshInterval,

              };

          }

      }

  }

  

注意看两个if语句,才发现OpenIdConnectOptions的Authority和MetadataAddress在都没有设置的情况下,OpenIdConnectOptions的ConfigurationManager为空!

    所以,从上文看得出,如果不设置OpenIdConnectOptions的Authority,那么无法进行OpenIdConnect认证哦!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于PHP+MYSQL酒店管理系统的设计与开发

随着人们生活条件的提高,旅游和出差已经成了家常便饭,但是因为他向异地所以第一个要解决的问题就是吃住问题,吃先对是比较好解决的一个问题,随便一个超市或者饭店甚至地摊就能解决这一问题,但是总不能露宿街头吧,所以很多人在到达目的地的第一时间就是去寻找一个能够入住的酒店…

一文彻底搞懂Mysql索引优化

专属小彩蛋&#xff1a;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff08;前言 - 床长人工智能教程&#xff09; 目录 一、索引介绍 二、性能分析 三、查询优化 一、索引介绍…

Python使用矩阵分解法推荐系统找到类似的音乐

这篇文章是如何使用几种不同的矩阵分解算法计算相关艺术家。最近我们被客户要求撰写关于的矩阵分解法推荐系统研究报告&#xff0c;包括一些图形和统计输出。代码用Python编写&#xff0c;以交互方式可视化结果。 加载数据 这可以使用Pandas加载到稀疏矩阵中&#xff1a; # r…

Jmeter 使用BeanShell断言,实现自动获取文章列表,并判断文章是否为当天发布的

系列文章目录 提示&#xff1a;阅读本章之前&#xff0c;请先阅读目录 文章目录系列文章目录前言一、正则表达式提取器&#xff0c;提取所有文章id二、循环控制器&#xff0c;读取每篇文章的创建时间三、JSON提取器&#xff0c;提取创建时间&#xff0c;然后进行判断四、运行结…

Java设计模式七大原则-里氏替换原则

里氏替换原则 OO中的继承性的思考和说明 继承包含这样一层含义&#xff1a;父类中凡是已经实现好的方法&#xff0c;实际上是在设定规范和契约&#xff0c;虽然它不强制要求所有的子类必须遵循这些契约&#xff0c;但是如果子类对这些已经实现的方法任意修改&#xff0c;就会对…

1540_AURIX_TriCore内核架构_FPU

全部学习汇总&#xff1a; GreyZhang/g_tricore_architecture: some learning note about tricore architecture. (github.com) 这一次看一下TriCore的FPU&#xff0c;浮点处理单元。说起来&#xff0c;这算是很多MCU中的一个高级模块了。 1. 在TriCore中FPU其实是可选实现的&a…

动画演示选择排序(Selection Sort)

1、排序规则 1.1 一句话总结选择排序 从数组中第一个数字开始&#xff0c;数组中每个数字都要和后面所有数字比一次大小&#xff0c;每每次循环遍历当前最小值&#xff0c;放在当前循环范围内的最小位置。当完成第 N - 1 次循环之后&#xff0c;排序完成。N 数组长度 - 1。 …

113.(leaflet篇)leaflet根据距离截取线段

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

不同类型的 SSL 证书解释

了解不同类型的 SSL 证书&#xff1a;扩展验证 (EV)、组织验证 (OV) 和域名验证 (DV)。 查看用例及更多。 SSL/TLS 证书用于验证网站的身份并在服务器和浏览器之间创建安全连接。有许多不同类型的 SSL 证书选项可用&#xff0c;它们都有其独特的用例和价值主张。证书颁发机构 …

【项目实战合集】计算机视觉毕业设计项目怎么选,超30个经典案例供你选择...

每年到了搞毕业设计的时候&#xff0c;很多学生朋友都很头疼&#xff0c;指导老师给不了好题目&#xff0c;自己也没有什么好的想法&#xff0c;怕选的太容易了过不了&#xff0c;怕选的太难了做不出&#xff01;今年我们在计算机视觉方向出了【超过30个基于Pytorch框架】的实战…

FineReport 动态图表表格软件-函数计算组成和语法

1. 概述 1.1 版本 1.2 功能简介 在设计模板时用户需要频繁的使用公式函数&#xff0c;例如&#xff1a;求和、求个数、做判断等等。 本文介绍函数的计算组成和语法。 2. 计算语法 2.1 概览 组成部分 语法 示例 函数 函数语法详情查看对应函数&#xff1a; SUM(合同金额…

AGI意识科学每周速递 | 2022年11月第四期

AGI&意识科学每周速递 | 2022年11月第四期 心识研究院 Mindverse Research 2022-11-28 17:00 发表于上海 收录于合集#AGI&意识科学每周速递24个 本周主要内容&#xff1a;程序辅助语言模型 PAL、AI 外交官 CICERO、视觉语言图灵测试、NLP 的持续学习、胎儿的大脑皮层…

winograd卷积实践

winograd卷积基本原理参考 Winograd算法实现卷积原理_Luchang-Li的博客-CSDN博客_optimizing batched winograd convolution on gpus winograd卷积图示&#xff1a; 注意这张图里面隐藏了input和output channel。实际上每个空间维度里面还包含了batch和in/out channel维度。 …

从pom文件里面找不到对应的Maven依赖,通过下面的方法完美解决

如下&#xff0c;我想获取gson对应的依赖 第一步&#xff1a;进入引入对应包的类里面 第二步&#xff1a;进入包&#xff1a;Ctrl 左键 ctrl左键点击gson后&#xff0c;会自动跳转到这个文件夹 第三步&#xff1a;打开依赖图 按箭头点击后&#xff0c;会出现下面的依赖图 …

[附源码]Python计算机毕业设计SSM基于Java的音乐网站(程序+LW)

环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 Maven管理等…

基于java+swing+mysql北方传统民居信息管理系统

基于javaswingmysql北方传统民居信息管理系统一、系统介绍二、功能展示1.用户登陆2.用户界面3.管理员界面4.民居信息修改5.民居信息详情三、系统实现1.ManageMainFrame.java四、其它1.其他系统实现一、系统介绍 用户&#xff1a;民居信息浏览、民居详细信息 管理员&#xff1a…

CDMP证书是什么样?CDMP证书有用吗?

随着数字化经济在我国的迅速开展&#xff0c;企业越来越重视数字人才的培养&#xff0c;致使越来越多得数字人通过考取CDMP证书来证明自己的能力。而一些犹豫观望的人&#xff0c;就会问。拿到CDMP证书&#xff0c;对你们真的有用吗&#xff1f;是纸质版证书还是电子版证书&…

PHP交流管理系统wamp运行定制开发mysql数据库html网页算机软件工程

一、源码特点 PHP交流管理系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库系统主要采用B/S模式开发,开发环境为PHP APACHE&#xff0c;数据库为mysql5.0 &#xff0c;使用php语言开发 PHP交流管理系统wamp运…

想把iPad作为扩展屏,却发现macOS monterey随航功能不见了

居家办公最不爽的事情就是没有扩展屏&#xff0c;对于开发来说&#xff0c;效率是有影响的&#xff0c;于是便想着把iPad当作扩展屏来用 系统参数 mac&#xff1a; macOS monterey&#xff08;12.4&#xff09;&#xff1b;M1 iPad&#xff1a; iPad Pro 第2代&#xff0c;应该…