AspNetCore3.1_Middleware源碼解析_3_HttpsRedirection


概述

上文提到3.1版本默認沒有使用Hsts,但是使用了這個中間件。看名字就很好理解,https跳轉,顧名思義,就是跳轉到
https地址。

使用場景,當用戶使用http訪問網站時,自動跳轉到https地址。這樣更加安全,不需要用戶特意輸入https://協議。

具體做了些我們一起來看看。

  app.UseHttpsRedirection();

使用方法

跟Hsts一樣,HttpsRedirection默認是不需要注入的,除非你需要修改默認配置。

services.AddHttpsRedirection(config =>
  {
      //https地址的端口號,默認null
      config.HttpsPort = 12345;

      //跳轉響應的狀態碼,默認307
      config.RedirectStatusCode = 302;
  });

直接使用中間件即可

 app.UseHttpsRedirection();

源碼解析

源代碼很簡單,只有兩個類:HttpsRedirectionOptions配置類,HttpsRedirectionMiddleware中間件

HttpsRedirectionOptions就只有兩個配置項

  /// <summary>
  /// Options for the HttpsRedirection middleware
  /// </summary>
  public class HttpsRedirectionOptions
  {
      /// <summary>
      /// The status code used for the redirect response. The default is 307.
      /// </summary>
      public int RedirectStatusCode { get; set; } = StatusCodes.Status307TemporaryRedirect;

      /// <summary>
      /// The HTTPS port to be added to the redirected URL.
      /// </summary>
      /// <remarks>
      /// If the HttpsPort is not set, we will try to get the HttpsPort from the following:
      /// 1. HTTPS_PORT environment variable
      /// 2. IServerAddressesFeature
      /// If that fails then the middleware will log a warning and turn off.
      /// </remarks>
      public int? HttpsPort { get; set; }
  }

重點看下中間件做了些什么。代碼量很少,大體是這些邏輯。

  • 如果請求是Https,跳過本中間件
  • 中間件會依次嘗試從這三個地方取端口號:HttpsRedirectionOptions的配置,HttpsRedirectionOptions,HTTPS_PORT環境變量或配置,IServerAddressesFeature(如果Webhost上綁定了https地址,本中間件能夠解析出來端口號)。
  • 如果沒有解析出來https的端口號,則跳過本中間件。
  • 如果能夠解析出來https端口號,則拼接出來https地址,返回307跳轉響應報文(或者配置的其他狀態碼)。

注:3.1同時支持HTTPS_PORT和ANCM_HTTPS_PORT這兩個環境變量。
image

https://docs.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.0

public class HttpsRedirectionMiddleware
    {
        private const int PortNotFound = -1;

        private readonly RequestDelegate _next;
        private readonly Lazy<int> _httpsPort;
        private readonly int _statusCode;

        private readonly IServerAddressesFeature _serverAddressesFeature;
        private readonly IConfiguration _config;
        private readonly ILogger _logger;

        /// <summary>
        /// Initializes the HttpsRedirectionMiddleware
        /// </summary>
        /// <param name="next"></param>
        /// <param name="options"></param>
        /// <param name="config"></param>
        /// <param name="loggerFactory"></param>
        public HttpsRedirectionMiddleware(RequestDelegate next, IOptions<HttpsRedirectionOptions> options, IConfiguration config, ILoggerFactory loggerFactory)

        {
            _next = next ?? throw new ArgumentNullException(nameof(next));
            _config = config ?? throw new ArgumentNullException(nameof(config));

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            var httpsRedirectionOptions = options.Value;
            if (httpsRedirectionOptions.HttpsPort.HasValue)
            {
                _httpsPort = new Lazy<int>(() => httpsRedirectionOptions.HttpsPort.Value);
            }
            else
            {
                _httpsPort = new Lazy<int>(TryGetHttpsPort);
            }
            _statusCode = httpsRedirectionOptions.RedirectStatusCode;
            _logger = loggerFactory.CreateLogger<HttpsRedirectionMiddleware>();
        }

        /// <summary>
        /// Initializes the HttpsRedirectionMiddleware
        /// </summary>
        /// <param name="next"></param>
        /// <param name="options"></param>
        /// <param name="config"></param>
        /// <param name="loggerFactory"></param>
        /// <param name="serverAddressesFeature">The</param>
        public HttpsRedirectionMiddleware(RequestDelegate next, IOptions<HttpsRedirectionOptions> options, IConfiguration config, ILoggerFactory loggerFactory,
            IServerAddressesFeature serverAddressesFeature)
            : this(next, options, config, loggerFactory)
        {
            _serverAddressesFeature = serverAddressesFeature ?? throw new ArgumentNullException(nameof(serverAddressesFeature));
        }

        /// <summary>
        /// Invokes the HttpsRedirectionMiddleware
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext context)
        {
            if (context.Request.IsHttps)
            {
                return _next(context);
            }

            var port = _httpsPort.Value;
            if (port == PortNotFound)
            {
                return _next(context);
            }

            var host = context.Request.Host;
            if (port != 443)
            {
                host = new HostString(host.Host, port);
            }
            else
            {
                host = new HostString(host.Host);
            }

            var request = context.Request;
            var redirectUrl = UriHelper.BuildAbsolute(
                "https", 
                host,
                request.PathBase,
                request.Path,
                request.QueryString);

            context.Response.StatusCode = _statusCode;
            context.Response.Headers[HeaderNames.Location] = redirectUrl;

            _logger.RedirectingToHttps(redirectUrl);

            return Task.CompletedTask;
        }

        //  Returns PortNotFound (-1) if we were unable to determine the port.
        private int TryGetHttpsPort()
        {
            // The IServerAddressesFeature will not be ready until the middleware is Invoked,
            // Order for finding the HTTPS port:
            // 1. Set in the HttpsRedirectionOptions
            // 2. HTTPS_PORT environment variable
            // 3. IServerAddressesFeature
            // 4. Fail if not sets

            var nullablePort = _config.GetValue<int?>("HTTPS_PORT") ?? _config.GetValue<int?>("ANCM_HTTPS_PORT");
            if (nullablePort.HasValue)
            {
                var port = nullablePort.Value;
                _logger.PortLoadedFromConfig(port);
                return port;
            }

            if (_serverAddressesFeature == null)
            {
                _logger.FailedToDeterminePort();
                return PortNotFound;
            }

            foreach (var address in _serverAddressesFeature.Addresses)
            {
                var bindingAddress = BindingAddress.Parse(address);
                if (bindingAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
                {
                    // If we find multiple different https ports specified, throw
                    if (nullablePort.HasValue && nullablePort != bindingAddress.Port)
                    {
                        _logger.FailedMultiplePorts();
                        return PortNotFound;
                    }
                    else
                    {
                        nullablePort = bindingAddress.Port;
                    }
                }
            }

            if (nullablePort.HasValue)
            {
                var port = nullablePort.Value;
                _logger.PortFromServer(port);
                return port;
            }

            _logger.FailedToDeterminePort();
            return PortNotFound;
        }
    }

OK,完成了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM