NetCore OpenIdConnect验证为什么要设置Authority?
创始人
2024-02-27 01:22:26
0

 在使用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 configureOptions)

       {

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

           return builder.AddRemoteScheme(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

///

        /// Adds a which can be used by .

        ///

        /// The type to configure the handler."/>.

        /// The used to handle this scheme.

        /// The name of this scheme.

        /// The display name of this scheme.

        /// Used to configure the scheme options.

        /// The builder.

        public virtual AuthenticationBuilder AddScheme(string authenticationScheme, string? displayName, Action? configureOptions)

            where TOptions : AuthenticationSchemeOptions, new()

            where THandler : AuthenticationHandler

            => AddSchemeHelper(authenticationScheme, displayName, configureOptions);        private AuthenticationBuilder AddSchemeHelper(string authenticationScheme, string? displayName, Action? configureOptions)

            where TOptions : AuthenticationSchemeOptions, new()

            where THandler : class, IAuthenticationHandler

        {

            Services.Configure(o =>

            {

                //注册Scheme对应的HandlerType

                o.AddScheme(authenticationScheme, scheme => {

                    scheme.HandlerType = typeof(THandler);

                    scheme.DisplayName = displayName;

                });

            });

            if (configureOptions != null)

            {

                Services.Configure(authenticationScheme, configureOptions);

            }

            //Options验证

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

                o.Validate(authenticationScheme);

                return true;

            });

            Services.AddTransient();

            return this;

        }

  

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

1

2

3

4

Services.AddOptions(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

///

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

       ///

       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 configureOptions)

     {

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

         return builder.AddRemoteScheme(authenticationScheme, displayName, configureOptions);

     }  

看得出AuthenticationBuilder的Services添加了一个名为OpenIdConnectPostConfigureOptions的单例服务: builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, 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

///

  /// Invoked to post configure a TOptions instance.

  ///

  /// The name of the options instance being configured.

  /// The options instance to configure.

  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(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(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认证哦!

 

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...