詳解ASP.NET Core 反向代理部署知多少
引言
最近在折騰統(tǒng)一認(rèn)證中心,看到開源項(xiàng)目IdentityServer4.Admin集成了IdentityServer4和管理面板,就直接拿過來用了。在嘗試Nginx部署時(shí)遇到了諸如虛擬目錄映射,請求頭超長、基礎(chǔ)路徑映射有誤等問題,簡單記錄,以供后人參考。
Nginx 配置路由轉(zhuǎn)發(fā)
首先來看下IdentityServer4.Admin的項(xiàng)目結(jié)構(gòu):
IdentityServer4.Admin / ├── Id4.Admin.Api # 用于提供訪問Id4資源的WebApi項(xiàng)目 ├── Id4.Admin # 用于提供管理Id4資源的Web管理面板 ├── Id4.STS.Identity # 用于提供 STS 服務(wù)的Web項(xiàng)目
作為三個(gè)獨(dú)立的項(xiàng)目,分開部署很簡單,但為了統(tǒng)一入口管理,我傾向于將Id4.Admin
和Id4.STS.Identity
部署在一個(gè)域名之下,Id4.Admin.API
項(xiàng)目部署到網(wǎng)關(guān)中去。也就是通過http://auth.xxx.com
訪問Id4.STS.Identity
,通過http://auth.xxx.com/admin
訪問Id4.Admin
。
這也就是遇到的第一個(gè)問題如何借助Nginx實(shí)現(xiàn)單域名多站點(diǎn)部署!
Kestrel作為一個(gè)邊緣web服務(wù)器部署時(shí),其將獨(dú)占一個(gè)IP和端口。在沒有反向代理服務(wù)器的情況下,用作邊緣服務(wù)器的Kestrel不支持在多個(gè)進(jìn)程之間共享相同的IP和端口。當(dāng)將Kestrel配置為在端口上偵聽時(shí),Kestrel將處理該端口的所有網(wǎng)絡(luò)通信,并且忽略請求頭中指定的Host
請求頭,也就意味著Kestrel 不會(huì)負(fù)責(zé)請求轉(zhuǎn)發(fā)。
因此為了進(jìn)行端口共享,我們需借助反向代理將唯一的IP和端口上將請求轉(zhuǎn)發(fā)給Kestrel。也就是下面這張圖。
根據(jù)Nginx 官方配置文檔,通過配置Location就可以實(shí)現(xiàn)指定路徑路由轉(zhuǎn)發(fā)。
server { listen 80; listen [::]:80; server_name mysite; location / { proxy_pass http://id4.sts.identity:80; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /admin/ { proxy_pass http://id4.admin:80/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
我們 比較下兩個(gè)proxy_pass的配置:
- location / { proxy_pass http://id4.sts.identity:80; }
- location /admin/ { proxy_pass http://id4.admin:80/; }
主要的不同點(diǎn)是 location /admin/
節(jié)點(diǎn)下proxy_pass http://id4.admin:80/
結(jié)尾包含一個(gè)左斜杠 /。(如果沒有這個(gè)左斜杠,所有的請求都會(huì)被路由到根節(jié)點(diǎn)。)比如有個(gè)請求http://auth.xxx.com/admin/dashboard
,那么nginx根據(jù)以上配置會(huì)將請求路由到http://id4.admin:80/dashboard
。也就是最后一個(gè)左斜杠會(huì)將替換掉 location 指定的路由規(guī)則,也就是這里的/admin
。
但這樣就OK了嗎?Absolutely no!執(zhí)行nginx -s reload
你將會(huì)得到一個(gè)大大的404
。
啟用 UsePathBase 中間件
這時(shí)就要用到UsePathBase中間件了,其作用就是設(shè)置站點(diǎn)請求基礎(chǔ)路徑。在Web項(xiàng)目中添加UsePathBase
中間件很簡單,首先在appsettings.json
中添加一個(gè)配置項(xiàng)PATHBASE
,然后Startup的Config中啟用就好。
appsettings.json { "PATHBASE":"/admin" } ----- public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } private IConfiguration Configuration { get; } // ... public void Configure(...) { // ... app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
啟用 UseForwardedHeaders 中間件
使用反向代理還有一個(gè)問題要注意,那就是反向代理會(huì)模糊一些請求信息:
- 通過HTTP代理HTTPS請求時(shí),原始傳輸協(xié)議(HTTPS)丟失,必須在請求頭中轉(zhuǎn)發(fā)。
- 由于應(yīng)用程序是從代理服務(wù)器收到請求的,而不是真正的請求來源,因此原始客戶端IP地址也必須在請求頭中轉(zhuǎn)發(fā)。
這也就是為什么上面的Nginx 配置,會(huì)默認(rèn)包含以下兩項(xiàng)配置的原因。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
Nginx已經(jīng)默認(rèn)配置轉(zhuǎn)發(fā)了以上信息,那么自然要顯式告知ASP.NET Core Web 應(yīng)用要從請求頭中取回真實(shí)的請求信息。配置很簡單,需要安`Microsoft.AspNetCore.HttpOverrides NuGet包,然后在Startup的Config中啟用中間件。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } private IConfiguration Configuration { get; } // ... public void Configure(...) { // ... app.UseForwardedHeaders(new ForwardedHeadersOptions{ ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
有一點(diǎn)必須注意,依賴于傳輸協(xié)議的任何組件,例如身份驗(yàn)證,鏈接生成,重定向和地理位置,都必須在請求頭轉(zhuǎn)發(fā)中間件之后啟用。通常,除了診斷和錯(cuò)誤處理中間件外,請求頭轉(zhuǎn)發(fā)中間件應(yīng)先于其他中間件運(yùn)行。
配置完成后,重新部署,對于一般的項(xiàng)目,應(yīng)該可以正常運(yùn)行了。但也可能遭遇:
解除 Nginx 請求頭轉(zhuǎn)發(fā)大小限制
針對這種錯(cuò)誤當(dāng)然要查Nginx錯(cuò)誤日志了,如果Nginx服務(wù)器部署在Linux服務(wù)器,那么默認(rèn)日志文件在/var/log/nginx/error.log
,日志如下:17677925 upstream sent too big header while reading response header from upstream。簡單翻譯就是請求頭數(shù)據(jù)過大。那我們就來看看轉(zhuǎn)發(fā)的請求頭到底會(huì)有多大,從下圖來看請求頭中攜帶的Cookie最大的有3K多。
nginx添加下面的配置即可:
proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k
重新加載Nginx 配置,訪問成功。
Is All Set? No!
修復(fù)基礎(chǔ)路徑錯(cuò)誤
當(dāng)我嘗試點(diǎn)擊Admin管理面板的鏈接時(shí),得到無情的404,因?yàn)殒溄拥刂窞椋?code>http://auth.xxx.com/configruaion/clients,正確的鏈接地址應(yīng)該是http://auth.xxx.com/admin/configruaion/clients
。也就是Razor TagHelper 渲染的<a asp-controller="Configruaion" asp-action="Clients">Manage Client</a>
,并沒有幫按照UsePathBase
指定的路徑生成a標(biāo)簽鏈接。咱們只能看看源碼一探究竟了Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs,最終在拼接Herf
屬性時(shí)使用的是var pathBase = ActionContext.HttpContext.Request.PathBase;
來拼接基礎(chǔ)路徑。也就是說說TagHelper根據(jù)Http請求上下文中獲取基礎(chǔ)路徑。因此如果采用location /admin/ { proxy_pass http://id4.admin:80/;
這種路由映射,最終會(huì)丟失原始路由的基礎(chǔ)路徑,也就是/admin/
路由部分。所以,我們還是乖乖把基礎(chǔ)路徑補(bǔ)充上,也就是proxy_pass http://id4.admin:80/admin/;
至此完成反向代理的單域名多站點(diǎn)部署。
最后
一波三折,但最終不負(fù)期望。最后完整Nginx配置放出,以供參考:
server { listen 80; listen [::]:80; server_name mysite; location / { proxy_pass http://id4.sts.identity:80; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /admin/ { proxy_pass http://id4.admin:80/admin/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } }
參考資料:
Configure ASP.NET Core to work with proxy servers and load balancers
GitHub Issue: Deploy to subdirectory #15464
ASP.Net Core 3 App Running under a Subdirectory on Nginx
到此這篇關(guān)于詳解ASP.NET Core 反向代理部署知多少的文章就介紹到這了,更多相關(guān)ASP.NET Core 反向代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用FlashPaper在線轉(zhuǎn)換.doc為.swf
Flashpaper的使用相信大多數(shù)人都知道,這里的Demo是用于在線轉(zhuǎn)換 .doc文件轉(zhuǎn)換為 .swf的flash文件。2011-02-02Asp.net mvc 數(shù)據(jù)調(diào)用示例代碼
Asp.net mvc 數(shù)據(jù)調(diào)用示例代碼,學(xué)習(xí)asp.net mvc框架的朋友可以參考下。2010-10-10C#反射技術(shù)的簡單操作(讀取和設(shè)置類的屬性)
反射的作用想必大家都知道了吧,少量屬性的自動(dòng)化操作手動(dòng)添加幾下當(dāng)然是沒有問題的,但是屬性數(shù)量較多的時(shí)候敲起這些繁鎖的代碼可以困了,再說對擴(kuò)展和維護(hù)性造成很多的不遍,以下代碼中如不能直接使用請?zhí)砑觰sing System.Text;的引用。2011-01-01asp.net Parameters.AddWithValue方法在SQL語句的 Where 字句中的用法
今天晚上看論壇,有人提問說,Parameters.AddWithValue方法在有些情況下不好使2009-01-01asp.net 通過aspnetpager為DataList分頁
今天整了半天才把DataList的分頁搞定,下面把我的設(shè)計(jì)過程給大家講講2009-12-12asp.net自定義控件回發(fā)數(shù)據(jù)實(shí)現(xiàn)方案與代碼
在實(shí)現(xiàn)asp.net的自定義控件中,若要實(shí)現(xiàn)數(shù)據(jù)的回發(fā)或者post數(shù)據(jù),那自義控件必須實(shí)現(xiàn)IPostBackDataHandler接口, 在該接口中有兩個(gè)方法一個(gè)是LoadPostData,另一個(gè)是RaisePostDataChangedEvent,需要的朋友可以了解下2012-12-12注冊表中存儲(chǔ)數(shù)據(jù)庫鏈接字符串的方法
2008-01-01asp.net+xml+flash實(shí)現(xiàn)的圖片展示效果示例
這篇文章主要介紹了asp.net+xml+flash實(shí)現(xiàn)的圖片展示效果的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了圖片展示效果的相關(guān)操作步驟與flash與xml調(diào)用的相關(guān)技巧,需要的朋友可以參考下2016-08-08