一、介紹
主機地址過濾中間件相當於一個白名單,標記哪些主機地址能訪問接口。
二、使用
新建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狀態碼。