全局中间件之 TrustProxies

未匹配的标注

简介

上一章,我们了解了 第四个 全局中间件 ConvertEmptyStringsToNull 的作用:将空字符串的字段转换为 null

这一章我们了解 最后一个 全局中间件 TrustProxies

从字面意思我们知道是 关于设置信任代理有关的中间件

file

Laravel 官方文档

关于设置信任代理的有关内容--->传送门

通过官方文档,我们知道:

  • 设置信任代理的 IP 地址,以及信任代理发过来的头信息都是在 App\Http\Middleware\TrustProxies 这个最后中间件中实现的。
  • 不知道代理 IP 的情况下,可以用 * 来解决问题

关于什么情况下设置信任代理

当你的站点(Laravel 所在服务器)位于代理服务器(例如:负载均衡)后面的时候,则每个 Web 请求都有可能始终来自该代理,而不是客户端实际在您的站点上发出请求。

此时,你应该根据代理服务设置的转发头以及代理服务器的 IP 地址来配置你的 Laravel 代理信任。

关于为什么要设置信任代理

如果您的站点位于代理(例如负载均衡器)后面,则您的 Web 应用程序可能存在以下一些问题:

  • 重定向和 PHP 生成的 URL 在其 Web 地址,协议和/或端口方面可能不准确。
  • 可能无法为每个用户创建唯一会话,从而导致可能访问不正确的帐户,或者无法让用户完全登录
  • 记录或其他数据收集过程数据可能看起来来自一个位置(代理本身),使您无法区分各个客户端所采取的流量/操作。

关于上面两个为什么的来源

官方扩展包 fideloper/proxy 关于代理的说法--->传送门

正式进入话题

我们来看一下 TrustProxies 是如何进行代理设置的

其 handle 方法在 TrustProxies 父类 Fideloper\Proxy\TrustProxies

Fideloper\Proxy\TrustProxies

<?php

namespace Fideloper\Proxy;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Contracts\Config\Repository;

class TrustProxies
{
    //...

    /**
     * 中间件过滤请求的核心方法
     */
    public function handle(Request $request, Closure $next)
    {
        $request::setTrustedProxies([], $this->getTrustedHeaderNames()); // Reset trusted proxies between requests
        $this->setTrustedProxyIpAddresses($request);

        return $next($request);
    }

    /**
     * 设置信任代理(例如:负载均衡)的 IP 地址
     */
    protected function setTrustedProxyIpAddresses(Request $request)
    {
        $trustedIps = $this->proxies ?: $this->config->get('trustedproxy.proxies');

        // Only trust specific IP addresses
        if (is_array($trustedIps)) {
            return $this->setTrustedProxyIpAddressesToSpecificIps($request, $trustedIps);
        }

        // Trust any IP address that calls us
        // `**` for backwards compatibility, but is depreciated
        if ($trustedIps === '*' || $trustedIps === '**') {
            return $this->setTrustedProxyIpAddressesToTheCallingIp($request);
        }
    }

    /**
     * 当知道代理服务器 IP 地址时,进行 IP 指定设置
     */
    private function setTrustedProxyIpAddressesToSpecificIps(Request $request, $trustedIps)
    {
        $request->setTrustedProxies((array) $trustedIps, $this->getTrustedHeaderNames());
    }

    /**
     * 当不知道代理服务器 IP 地址时,进行 * 全局设置
     */
    private function setTrustedProxyIpAddressesToTheCallingIp(Request $request)
    {
        $request->setTrustedProxies([$request->server->get('REMOTE_ADDR')], $this->getTrustedHeaderNames());
    }

    /**
     * 获取信任的头信息
     */
    protected function getTrustedHeaderNames()
    {
        return $this->headers ?: $this->config->get('trustedproxy.headers');
    }
    //...
}

大家可能看到 $request->setTrustedProxies() 方法被调用过好几次,这个方法就是设置信任代理的核心方法,但是非常简单,我们来看一下

Symfony\Component\HttpFoundation\Request

public static function setTrustedProxies(array $proxies, int $trustedHeaderSet)
{
    self::$trustedProxies = $proxies;
    self::$trustedHeaderSet = $trustedHeaderSet;
}

是不是很简单,就是设置两个静态属性。当后面需要设置重定向,保存日志,设置 session 时,应该会用到这个两个静态属性,具体需要我们一起学到才能知道。

最后,关于官方扩展包的另外一种设置信任 IP 和信任头的方法

  • config/app.php 配置文件中添加服务提供者

    'providers' => array(
      /*
       * 可信代理服务提供者
       */
      Fideloper\Proxy\TrustedProxyServiceProvider::class,
    );
  • 生成 config 配置文件

    $ php artisan vendor:publish --provider="Fideloper\Proxy\TrustedProxyServiceProvider"
  • 替换掉 App\Http\Middleware\TrustProxies 全局中间件

    App\Http\Kernel

    <?php
    
    namespace App\Http;
    
    use Illuminate\Foundation\Http\Kernel as HttpKernel;
    
    class Kernel extends HttpKernel
    {
      protected $middleware = [
          \App\Http\Middleware\CheckForMaintenanceMode::class,
          \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
          \App\Http\Middleware\TrimStrings::class . ':foo,bar',
          \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
          \Fideloper\Proxy\TrustProxies::class,  // 原先的是 \App\Http\Middleware\TrustProxies::class,
      ];
    
      // ...
    
    }
  • 现在我们就可以在 config 配置文件中进行 代理信任 配置了

    file

写在最后

由于没有用过 Laravel 关于信任代理方面的功能。上面解说,我仅从源代码和相关文档,整理得来,具体使用效果,有经验的童鞋,可以评论区发表出来,大家一起学习。

本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~