• .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状态码。

  • 相关阅读:
    生成括号问题(22)
    Starting Jetty: Exception in thread "main" java.lang.UnsupportedClassVersionError: org/eclipse/jetty/start/Main : Unsupported major.minor version 52.0
    Oracle 使用Nid 修改数据库的DBID 和 Database Name
    Oracle SCN与时间的相互转换
    Oracle 启动 停止JOB
    Apache 负载均衡 端口转发 配置
    Oracle 将 A 用户下所有表的增删改查 赋予 B 用户
    更改表空间及数据文件的名称
    Oracle 缓存命中率问题一则(里面有个问题咨询大佬们)
    更改python版本
  • 原文地址:https://www.cnblogs.com/MicroHeart/p/9672367.html
Copyright © 2020-2023  润新知