简介
Yarp 是微软团队开发的一个反向代理组件, 除了常规的 http 和 https 转换通讯,它最大的特点是可定制化,很容易根据特定场景开发出需要的定制代理通道。
详细介绍:https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release
源码仓库:https://github.com/microsoft/reverse-proxy
文档地址 :https://microsoft.github.io/reverse-proxy/
基础使用
1、创建 ASP.NET Core 空项目
使用 Visual Studio :
使用 .NET CLI 命令行创建:
1 | dotnet new web -o MyProxy |
2、 修改代码 Program.cs 文件
1 2 3 4 5 6 7 | var builder = WebApplication.CreateBuilder(args); builder.Services.AddReverseProxy() .LoadFromConfig(builder.Configuration.GetSection( "ReverseProxy" )); var app = builder.Build(); app.MapGet( "/Ping" , () => "Hello World!" ); app.MapReverseProxy(); app.Run(); |
3、修改配置文件 appsettings.json
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 | { "Logging" : { "LogLevel" : { "Default" : "Information" , "Microsoft.AspNetCore" : "Warning" } }, "AllowedHosts" : "*" , "ReverseProxy" : { "Routes" : { "routeAll" : { "ClusterId" : "clusterBaidu" , "Match" : { "Path" : "{**catch-all}" } } }, "Clusters" : { "clusterBaidu" : { "Destinations" : { "baidu" : { } } } } } } |
这里的配置是将所有的请求都转发到百度。
在 Program.cs 里,还注册了一个 Get 路由 Ping 。
4、启动项目
能够看到在浏览器访问程序监听的端口号后,显示的是百度的页面。打开 F12 ,看到请求头也是本地的,并不是百度的域名。
测试手动注册的路由 Ping :
能够显示正常。
5、问题整理
- (1) Yarp 是不是只能做这种简单的转发?
不是,往下有配置文件说明。
- (2) JSON 配置文件里有什么要注意的地方吗?
有。在这个演示的配置文件中 ReverseProxy:Clusters:cluster1:Destinations:destination1:Address 对应的值是:https://www.baidu.com/ ,如果去掉 www ,在项目启动后会跳转到百度首页,不是代理转发。去掉末尾的 / 符合没有任何影响。
- (3) Yarp 会影响到程序中注册的路由吗?
不会影响到程序内部注册的路由。在 Program.cs 中无论 app.MapReverseProxy(); 在上还是在下,在访问 Ping 的时候,都是返回 Hello World!
1 2 3 4 | var app = builder.Build(); app.MapReverseProxy(); app.MapGet( "/Ping" , () => "Hello World!" ); app.Run(); |
进阶探索
1、多地址代理
修改配置文件 appsettings.json ,实现默认路由跳转百度,当访问 /movie 是访问 b站。
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 | { "Logging" : { "LogLevel" : { "Default" : "Information" , "Microsoft.AspNetCore" : "Warning" } }, "AllowedHosts" : "*" , "ReverseProxy" : { "Routes" : { "routeBaidu" : { "ClusterId" : "clusterBaidu" , "Match" : { "Path" : "{**catch-all}" } }, "routeBiliBili" : { "ClusterId" : "clusterBiliBili" , "Match" : { "Path" : "/movie/{**catch-all}" } } }, "Clusters" : { "clusterBaidu" : { "Destinations" : { "baidu" : { } } }, "clusterBiliBili" : { "Destinations" : { "bilibili" : { } } } } } } |
测试结果:
在后面输入路由 /movie 后能够跳转到b站。但是b站网页没有完整显示,图片都没有,这是网站上的策略问题,对于数据接口没有这些问题。
详细的配置文件说明,可以查看 https://microsoft.github.io/reverse-proxy/articles/config-files.html
2、规则匹配
网页上太多资源,为了方便测试,启用两个 api 接口。地址分别是:http://localhost:5241/ 和 https://localhost:7184/
两个 api 接口中分别注册 /test 路由。
1 2 3 4 5 | app.MapGet( "/test" , () => "Welcome to Api111!" ); app.MapGet( "/test" , () => "Welcome to Api222!" ); |
启动两个 api 程序。
1 2 3 4 5 | C:UsersTest>curl http: //localhost :5241 /test Welcome to Api111! C:UsersTest>curl https: //localhost :7184 /test Welcome to Api222! |
修改 MyProxy 项目的配置文件 appsettings.json
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 | { "Logging" : { "LogLevel" : { "Default" : "Information" , "Microsoft.AspNetCore" : "Warning" } }, "AllowedHosts" : "*" , "ReverseProxy" : { "Routes" : { "routeOne" : { "ClusterId" : "clusterOne" , "Match" : { "Path" : "/test/{**catch-all}" , "QueryParameters" : [ { "Name" : "number" , "Values" : [ "1" ] } ] } }, "routeTwo" : { "ClusterId" : "clusterTwo" , "Match" : { "Path" : "/test/{**catch-all}" , "QueryParameters" : [ { "Name" : "number" , "Values" : [ "2" ] } ] } }, "routeBaidu" : { "ClusterId" : "clusterBaidu" , "Match" : { "Path" : "{**catch-all}" } } }, "Clusters" : { "clusterOne" : { "Destinations" : { "apiOne" : { } } }, "clusterTwo" : { "Destinations" : { "apiTwo" : { } } }, "clusterBaidu" : { "Destinations" : { "baidu" : { } } } } } } |
- Path :监听路由地址。
- QueryParameters:匹配参数。
- QueryParameters:Name:参数名。
- QueryParameters:Values:参数值。
MyProxy 的监听端口是 http://localhost:5024/ 访问结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | C:UsersTest>curl http: //localhost :5024 /ping Hello World! C:UsersTest>curl http: //localhost :5024 /test <title>404 Not Found< /title ><h1>Not Found< /h1 > <p>The requested URL /test was not found on this server.< /p > C:UsersTest>curl http: //localhost :5024 /test ?number=1 Welcome to Api111! C:UsersTest>curl http: //localhost :5024 /test ?number=2 Welcome to Api222! |
能够根据参数以及参数值导向对应的地址。
3、问题整理
- (1)为什么访问 /movie 不能正常显示网页。
因为 b站某些接口开启了防盗链,还有跨域检测。
- (2)在根据参数匹配中,如果匹配的路由一样,监听的参数一样,参数值也一样会怎么样?
访问该路由地址会报错。
- (3)路由匹配的优先级?
程序内注册的路由优先级最高,其次才是 Yarp 在配置文件里加载的。
小试牛刀
最近的工作是做企业内数据安全方面的。推动公司数据安全体系,通过技术手段提升公司信息安全。
有一个很老的OA系统,十几年了, .NET Framework 2.0 写的。漏洞一大堆,包括不限于xss、sql注入等,权限只到表单级别。浏览器上按下 F12 能查看到表单链接,直接复制出去,别人也能访问。
在这个系统上要做安全,我想的是在中间加代理,正好适合使用 Yarp 来完成,也方便写业务处理代码。嗯,很真实, .NET Core 写的,方便写业务代码。
用户登录成功后,会记录下用户的 Host 和 Cookie,每次访问的时候系统的时候,在 Yarp 这里都校验一下是否与用户登录时的匹配。
解决了两个问题:
1、从网络层捕获到所有的请求,方便后面做排查。参数、传值,出了事故可以找到责任人。
2、隔离真实的站点地址,杜绝弱安全等级网站暴露后被坏人攻击的风险。
踩坑集锦
1、non-ASCII
项目要代理某网页,在使用下载功能的时候,接口返回 502 。
1 2 3 4 5 6 7 8 9 10 11 | info: Yarp.ReverseProxy.Forwarder.HttpForwarder[48] ResponseHeaders: The destination returned a response that cannot be proxied back to the client. System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x00E4 at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowInvalidHeaderCharacter(Char ch) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(StringValues headerValues) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.SetValueFast(String key, StringValues value) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value) at Yarp.ReverseProxy.Forwarder.HttpTransformer.CopyResponseHeaders(HttpHeaders source, IHeaderDictionary destination) at Yarp.ReverseProxy.Forwarder.HttpTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse) at Yarp.ReverseProxy.Transforms.Builder.StructuredTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse) at Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, String destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer) |
去 GitHub 翻 Issues
下载接口能正常访问,文件流也能完整地拿到。重写了所有的响应头没有用。这种不开源的商业站点,也猜不到字符编码。
最后妥协了,用了一个 .NET 服务在服务器上下载后再转发。
代理非常规服务接口时,一定要多测试。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对IT俱乐部的支持。如果你想了解更多相关内容请查看下面相关链接