request->server('remote_addr'); if (! $this->isFromTrustedProxy()) return [$ip]; return $this->getTrustedValues(ClientIpRequestConstant::HEADER_X_FORWARDED_FOR, $ip) ?: [$ip]; } /** * 判断此请求是否来自受信任的代理。这可以用于确定是否信任 * @return bool */ private function isFromTrustedProxy(): bool { return ( self::$trustedProxies && IpUtils::checkIp($this->request->server('remote_addr',''),self::$trustedProxies) || self::isTrustedRemoteAddr() ); } /** * @param int $type * @param string|null $ip * @return array|mixed|null[]|string[] */ private function getTrustedValues(int $type, ?string $ip = null): mixed { $cacheKey = $type . "\0" . ((self::$trustedHeaderSet & $type) ? $this->request->getHeaderLine(ClientIpRequestConstant::TRUSTED_HEADERS[$type]) : ''); $cacheKey .= "\0" . $ip . "\0" . $this->request->getHeaderLine(ClientIpRequestConstant::TRUSTED_HEADERS[ClientIpRequestConstant::HEADER_FORWARDED]); if (isset($this->trustedValuesCache[$cacheKey])) return $this->trustedValuesCache[$cacheKey]; $clientValues = []; $forwardedValues = []; if ( (self::$trustedHeaderSet & $type) && $this->request->hasHeader(ClientIpRequestConstant::TRUSTED_HEADERS[$type]) ) { foreach (explode(',', $this->request->getHeaderLine(ClientIpRequestConstant::TRUSTED_HEADERS[$type])) as $value) { $clientValues[] = ($type === ClientIpRequestConstant::HEADER_X_FORWARDED_PORT ? '0.0.0.0' : trim($value)); } } if ( (self::$trustedHeaderSet & ClientIpRequestConstant::HEADER_FORWARDED) && (isset(ClientIpRequestConstant::FORWARDED_PARAMS[$type])) && $this->request->hasHeader(ClientIpRequestConstant::TRUSTED_HEADERS[ClientIpRequestConstant::HEADER_FORWARDED]) ) { $forward = $this->request->getHeaderLine(ClientIpRequestConstant::TRUSTED_HEADERS[ClientIpRequestConstant::HEADER_FORWARDED]); $parts = HeaderUtils::split($forward, ',;='); $param = ClientIpRequestConstant::FORWARDED_PARAMS[$type]; foreach ($parts as $subParts) { if (($value = HeaderUtils::combine($subParts)[$param] ?? null) === null) continue; if ($type === ClientIpRequestConstant::HEADER_X_FORWARDED_PORT) { if ( str_ends_with($value, ']') || ($value = mb_strrchr($value, ':')) === false ) $value = $this->isSecure() ? ':443' : ':80'; $value = '0.0.0.0' . $value; } $forwardedValues[] = $value; } } if (null !== $ip) { $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip); $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip); } if ($forwardedValues === $clientValues || !$clientValues) return $this->trustedValuesCache[$cacheKey] = $forwardedValues; if (!$forwardedValues) return $this->trustedValuesCache[$cacheKey] = $clientValues; if (!$this->isForwardedValid) return (($ip = $this->trustedValuesCache[$cacheKey]) !== null) ? ['0.0.0.0',$ip] : []; $this->isForwardedValid = false; throw new ErrException('转发报头无效,请检查服务器配置。'); } /** * @return bool */ private function isSecure(): bool { return $this->request->getHeaderLine(ClientIpRequestConstant::HEADER_X_FORWARDED_PROTO) === 'https' || ($this->request->getServerParams()['https'] ?? '') === 'on'; } /** * @param array $clientIps * @param string $ip * @return array|null[]|string[] */ private function normalizeAndFilterClientIps(array $clientIps, string $ip): array { if (!$clientIps) return []; $clientIps[] = $ip; $firstTrustedIp = null; foreach ($clientIps as $key => $clientIp) { if (mb_strpos($clientIp, '.')) { // ipv4 $i = mb_strpos($clientIp, '.'); if ($i) $clientIps[$key] = $clientIp = mb_substr($clientIp, 0, $i); } elseif (str_starts_with($clientIp, '[')) { // ipv6 $i = mb_strpos($clientIp, ']',1); $clientIps[$key] = $clientIp = mb_substr($clientIp, 1, $i - 1); } if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { unset($clientIps[$key]); continue; } if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { unset($clientIps[$key]); $firstTrustedIp ??= $clientIp; } } return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp]; } }