.Net Core 中間件之主機地址過濾(HostFiltering)源碼解析

一、介紹

  主機地址過濾中間件相當於一個白名單,標記哪些主機地址能訪問接口。

二、使用

  新建WebAPI項目,修改Startup中的代碼段如下所示。下面表示允許主機名爲“localhost”的主機訪問(不區分大小寫),其他主機地址訪問此項目的接口都會返回400錯誤。

    public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            var hosts = new List<string>();
            hosts.Add("localhost");
       //添加配置
            services.AddHostFiltering(x =>
            {
                x.AllowedHosts = hosts;//設置允許訪問的Host 最少需要設置一個
                x.AllowEmptyHosts = false;//是否允許請求頭Host的值爲空訪問  默認值爲true
                x.IncludeFailureMessage = true;//true表示返回錯誤信息,false表示返回空 默認爲true
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
       //使用中間件 app.UseHostFiltering(); app.UseMvc(); }

(正常訪問)

上面爲正常訪問情況,爲了模擬通過其他域名訪問此接口,配置Host文件,在Host文件末尾加上下面一句話,通過地址https://baidu.com:5001/api/values來訪問https://localhost:5001/api/values,這樣做的目的是改變瀏覽器請求頭中的Host的值爲baidu.com。

localhost       baidu.com

(返回400)

由於請求頭中沒有填寫Host信息,瀏覽器自動解析地址,將baidu.com作爲Host的值,由於baidu.com不在我們定義的允許訪問的主機地址中,所以返回400。但是我們可以僞造請求頭,如下圖:

(僞造請求頭)

也可以直接將Host的值爲空,因爲上面配置的是運行爲空的主機名訪問。

三、源碼解析

  既然是中間件,最重要的一個方法就是Invoke,因爲這個方法會在每個請求中被調用。

    public Task Invoke(HttpContext context)
        {
            var allowedHosts = EnsureConfigured();//獲取配置的允許訪問的Host集合列表

            if (!CheckHost(context, allowedHosts))//通過Http上下文,獲取當前請求的Host,並判斷是否在運行訪問的Host列表中
            {
                context.Response.StatusCode = 400;//如果不在訪問列表中,返回400,並判斷是否返回錯誤信息,如果返回錯誤信息,就將錯誤信息寫到報文體中
                if (_options.IncludeFailureMessage)
                {
                    context.Response.ContentLength = DefaultResponse.Length;
                    context.Response.ContentType = "text/html";
                    return context.Response.Body.WriteAsync(DefaultResponse, 0, DefaultResponse.Length);
                }
                return Task.CompletedTask;
            }

            return _next(context);//委託 調用下一個中間件
        }

        private IList<StringSegment> EnsureConfigured()
        {
            if (_allowAnyNonEmptyHost == true || _allowedHosts?.Count > 0)//判斷配置的是不是允許爲空
            {
                return _allowedHosts;
            }

            var allowedHosts = new List<StringSegment>();
            if (_options.AllowedHosts?.Count > 0 && !TryProcessHosts(_options.AllowedHosts, allowedHosts))
            {
                _logger.LogDebug("Wildcard detected, all requests with hosts will be allowed.");
                _allowedHosts = allowedHosts;//將配置的值賦給_alloweHosts
                _allowAnyNonEmptyHost = true;
                return _allowedHosts;
            }
            if (allowedHosts.Count == 0)//Host至少配置一個
            {
                throw new InvalidOperationException("No allowed hosts were configured.");
            }
            if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("Allowed hosts: {Hosts}", string.Join("; ", allowedHosts));
            }
            _allowedHosts = allowedHosts;
            return _allowedHosts;
        }

這個中間件邏輯很簡單,總體來說就是判斷請求頭中的Host的值是否在配置的列表中,如果在,就下面一箇中間件繼續處理。如果不在,就返回400狀態碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章