mirror of
https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git
synced 2025-12-25 11:22:10 +08:00
Compare commits
2 Commits
8665b27294
...
1eae4171f6
| Author | SHA1 | Date | |
|---|---|---|---|
| 1eae4171f6 | |||
| 8046676669 |
82
app/Aspect/AdminReturnAspect.php
Normal file
82
app/Aspect/AdminReturnAspect.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Aspect;
|
||||
|
||||
use App\Lib\Log\Logger;
|
||||
use App\Lib\Return\AdminReturn;
|
||||
use App\Trait\ClientIpTrait;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Coroutine\Coroutine;
|
||||
use Hyperf\Di\Annotation\Aspect;
|
||||
use Hyperf\Di\Aop\AbstractAspect;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Hyperf\Di\Aop\ProceedingJoinPoint;
|
||||
|
||||
#[Aspect]
|
||||
class AdminReturnAspect extends AbstractAspect
|
||||
{
|
||||
use ClientIpTrait;
|
||||
|
||||
/**
|
||||
* 切入类
|
||||
* @var array|\class-string[]
|
||||
*/
|
||||
public array $classes = [
|
||||
AdminReturn::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected int $adminId = 0;
|
||||
|
||||
/**
|
||||
* @param Logger $logger
|
||||
* @param RequestInterface $request
|
||||
*/
|
||||
public function __construct(
|
||||
protected readonly Logger $logger,
|
||||
protected readonly RequestInterface $request,
|
||||
) {
|
||||
$this->adminId = Context::get('current_admin_id',0);
|
||||
}
|
||||
|
||||
public function process(ProceedingJoinPoint $proceedingJoinPoint)
|
||||
{
|
||||
// 直接从方法参数获取请求数据
|
||||
$requestData = $proceedingJoinPoint->getArguments()[0] ?? [];
|
||||
|
||||
// 执行原方法并获取返回值
|
||||
$responseData = $proceedingJoinPoint->process();
|
||||
|
||||
// 写日志
|
||||
$this->writeLog($requestData, $responseData);
|
||||
|
||||
// 返回
|
||||
return $requestData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $requestData
|
||||
* @param array $responseData
|
||||
* @return void
|
||||
*/
|
||||
private function writeLog(array $requestData = [], array $responseData = []): void
|
||||
{
|
||||
Coroutine::create(function () use ($requestData, $responseData) {
|
||||
$context = [
|
||||
'user_id' => $this->adminId,
|
||||
'method' => $this->request->getMethod(),
|
||||
'router' => $this->request->getUri(),
|
||||
'ip' => $this->getClientIp(),
|
||||
'request_data' => $requestData,
|
||||
'response_data' => $responseData,
|
||||
];
|
||||
|
||||
$this->logger->request()->info('admin_request_log', $context);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@ use Hyperf\Constants\EnumConstantsTrait;
|
||||
#[Constants]
|
||||
final class AdminCode extends ResultCode
|
||||
{
|
||||
#[Message("登录失败")]
|
||||
public const int LOGIN_ERROR = 10001;
|
||||
#[Message("账号已禁用")]
|
||||
public const int DISABLED = 423;
|
||||
|
||||
#[Message("暂无权限")]
|
||||
public const int FORBIDDEN = 403;
|
||||
|
||||
#[Message("验证已过期")]
|
||||
public const int LOGIN_TOKEN_ERROR = 10002;
|
||||
}
|
||||
|
||||
61
app/Constants/Common/ClientIpRequestConstant.php
Normal file
61
app/Constants/Common/ClientIpRequestConstant.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Common;
|
||||
|
||||
|
||||
final class ClientIpRequestConstant
|
||||
{
|
||||
/**
|
||||
* RFC 7239 标准的 Forwarded 头
|
||||
*/
|
||||
public const int HEADER_FORWARDED = 0b000001;
|
||||
|
||||
/**
|
||||
* 客户端原始 IP
|
||||
*/
|
||||
public const int HEADER_X_FORWARDED_FOR = 0b000010;
|
||||
|
||||
/**
|
||||
* 原始主机名
|
||||
*/
|
||||
public const int HEADER_X_FORWARDED_HOST = 0b000100;
|
||||
|
||||
/**
|
||||
* 原始协议 (http/https)
|
||||
*/
|
||||
public const int HEADER_X_FORWARDED_PROTO = 0b001000;
|
||||
|
||||
/**
|
||||
* 原始端口
|
||||
*/
|
||||
public const int HEADER_X_FORWARDED_PORT = 0b010000;
|
||||
|
||||
/**
|
||||
* 原始路径前缀
|
||||
*/
|
||||
public const int HEADER_X_FORWARDED_PREFIX = 0b100000;
|
||||
|
||||
/**
|
||||
* 将部分标志映射到 RFC 7239 Forwarded 头中的参数名
|
||||
*/
|
||||
public const array FORWARDED_PARAMS = [
|
||||
self::HEADER_X_FORWARDED_FOR => 'for',
|
||||
self::HEADER_X_FORWARDED_HOST => 'host',
|
||||
self::HEADER_X_FORWARDED_PROTO => 'proto',
|
||||
self::HEADER_X_FORWARDED_PORT => 'host',
|
||||
];
|
||||
|
||||
/**
|
||||
* 将标志映射到实际的 HTTP 头名称
|
||||
*/
|
||||
public const array TRUSTED_HEADERS = [
|
||||
self::HEADER_FORWARDED => 'forwarded',
|
||||
self::HEADER_X_FORWARDED_FOR => 'x-forwarded-for',
|
||||
self::HEADER_X_FORWARDED_HOST => 'x-forwarded-host',
|
||||
self::HEADER_X_FORWARDED_PROTO => 'x-forwarded-proto',
|
||||
self::HEADER_X_FORWARDED_PORT => 'x-forwarded-port',
|
||||
self::HEADER_X_FORWARDED_PREFIX => 'x-forwarded-prefix',
|
||||
];
|
||||
}
|
||||
@@ -6,18 +6,19 @@ namespace App\Controller\Admin;
|
||||
|
||||
use App\Annotation\Permission;
|
||||
use App\Annotation\ResponseFormat;
|
||||
use App\Middleware\Token\AdminTokenMiddleware;
|
||||
use App\Middleware\Admin\AdminTokenMiddleware;
|
||||
use App\Middleware\Admin\PermissionMiddleware;
|
||||
use App\Service\Admin\AdminUser\UserService;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\Middleware;
|
||||
use Hyperf\HttpServer\Annotation\RequestMapping;
|
||||
use Hyperf\Validation\Annotation\Scene;
|
||||
|
||||
|
||||
#[Controller(prefix: "admin/adminUser")]
|
||||
#[ResponseFormat('admin')]
|
||||
#[Middleware(AdminTokenMiddleware::class)]
|
||||
#[Middleware(middleware: AdminTokenMiddleware::class, priority: 100)]
|
||||
#[Middleware(middleware: PermissionMiddleware::class, priority: 99)]
|
||||
class AdminUserController
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Controller\Admin;
|
||||
|
||||
use App\Annotation\ResponseFormat;
|
||||
use App\Controller\AbstractController;
|
||||
use App\Middleware\Token\RefreshAdminTokenMiddleware;
|
||||
use App\Middleware\Admin\RefreshAdminTokenMiddleware;
|
||||
use App\Request\Admin\LoginRequest;
|
||||
use App\Service\Admin\Login\LoginService;
|
||||
use App\Service\Admin\Login\RefreshService;
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware\Token;
|
||||
namespace App\Middleware\Admin;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
use App\Exception\ErrException;
|
||||
use App\Interface\JwtInterface;
|
||||
use App\Middleware\Token\AbstractTokenMiddleware;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use function Hyperf\Support\env;
|
||||
107
app/Middleware/Admin/PermissionMiddleware.php
Normal file
107
app/Middleware/Admin/PermissionMiddleware.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware\Admin;
|
||||
|
||||
use App\Annotation\Permission;
|
||||
use App\Constants\AdminCode;
|
||||
use App\Constants\Model\AdminUser\AdminUserStatusCode;
|
||||
use App\Exception\ErrException;
|
||||
use App\Lib\Jwt\RequestScopedTokenTrait;
|
||||
use App\Model\AdminUser;
|
||||
use App\Trait\AdminUserTrait;
|
||||
use App\Trait\ParserRouterTrait;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Di\Annotation\AnnotationCollector;
|
||||
use Hyperf\HttpServer\Router\Dispatched;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class PermissionMiddleware implements MiddlewareInterface
|
||||
{
|
||||
use RequestScopedTokenTrait;
|
||||
use AdminUserTrait;
|
||||
use ParserRouterTrait;
|
||||
|
||||
/**
|
||||
* @var AdminUser
|
||||
*/
|
||||
protected AdminUser $adminUserInfo;
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(protected ContainerInterface $container) {}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$adminId = $this->getToken()?->claims()?->get(RegisteredClaims::ID) ?? 0;
|
||||
if ($adminId <= 0) throw new ErrException('账户不存在');
|
||||
|
||||
$this->adminUserInfo = $this->getAdminUserInfo($adminId);
|
||||
if ($this->adminUserInfo->status == AdminUserStatusCode::DISABLE) throw new ErrException('账号已禁用',AdminCode::DISABLED);
|
||||
|
||||
// 超级管理员提前下场 不用判断权限
|
||||
if ($this->adminUserInfo->isSuperAdmin()) return $handler->handle($request);
|
||||
|
||||
$this->check($request->getAttribute(Dispatched::class));
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatched $dispatched
|
||||
* @return bool
|
||||
*/
|
||||
private function check(Dispatched $dispatched): bool
|
||||
{
|
||||
$parseResult = $this->parse($dispatched->handler->callback);
|
||||
if (! $parseResult) return true;
|
||||
|
||||
[$controller, $method] = $parseResult;
|
||||
$annotations = AnnotationCollector::getClassMethodAnnotation($controller, $method);
|
||||
$classAnnotation = AnnotationCollector::getClassAnnotation($controller, Permission::class);
|
||||
/**
|
||||
* @var Permission[] $permissions
|
||||
*/
|
||||
$classAnnotation && $permissions[] = $classAnnotation;
|
||||
$methodPermission = Arr::get($annotations, Permission::class);
|
||||
$methodPermission && $permissions[] = $methodPermission;
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$this->handlePermission($permission);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Permission $permission
|
||||
* @return void
|
||||
*/
|
||||
private function handlePermission(Permission $permission): void
|
||||
{
|
||||
$operation = $permission->getOperation();
|
||||
$codes = $permission->getCode();
|
||||
|
||||
foreach ($codes as $code) {
|
||||
$isMenu = $this->adminUserInfo->hasPermission($code);
|
||||
|
||||
if ($operation === Permission::OPERATION_AND && !$isMenu) throw new ErrException('暂无权限',AdminCode::FORBIDDEN);
|
||||
|
||||
if ($operation === Permission::OPERATION_OR && $isMenu) return;
|
||||
}
|
||||
|
||||
if ($operation === Permission::OPERATION_OR) throw new ErrException('暂无权限',AdminCode::FORBIDDEN);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware\Token;
|
||||
namespace App\Middleware\Admin;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
use App\Exception\ErrException;
|
||||
use App\Interface\JwtInterface;
|
||||
use App\Middleware\Token\AbstractTokenMiddleware;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
||||
@@ -12,6 +12,7 @@ namespace App\Service\Admin;
|
||||
|
||||
use App\Lib\Jwt\RequestScopedTokenTrait;
|
||||
use App\Lib\Return\AdminReturn;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
@@ -46,6 +47,7 @@ abstract class BaseAdminService
|
||||
public function __construct()
|
||||
{
|
||||
$this->adminId = (int) $this->getToken()?->claims()?->get(RegisteredClaims::ID) ?? 0;
|
||||
if ($this->adminId > 0) Context::set('current_admin_id', $this->adminId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,10 @@ namespace App\Trait;
|
||||
|
||||
use App\Cache\Redis\RedisCache;
|
||||
use App\Cache\Redis\RedisKey;
|
||||
use App\Model\AdminUser;
|
||||
use App\Repository\AdminUserRepository;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Database\Model\Model;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
trait AdminUserTrait
|
||||
@@ -24,9 +26,9 @@ trait AdminUserTrait
|
||||
|
||||
/**
|
||||
* @param int $adminId
|
||||
* @return array|null
|
||||
* @return AdminUser|Model|mixed|string|null
|
||||
*/
|
||||
public function getAdminUserInfo(int $adminId): array|null
|
||||
public function getAdminUserInfo(int $adminId): mixed
|
||||
{
|
||||
$key = RedisKey::getAdminUserInfoKey($adminId);
|
||||
if (Context::has($key)) {
|
||||
@@ -34,16 +36,16 @@ trait AdminUserTrait
|
||||
}
|
||||
|
||||
if ($this->redis->with()->exists($key)) {
|
||||
$userInfo = $this->redis->with()->get($key);
|
||||
$userInfo = unserialize($this->redis->with()->get($key));
|
||||
Context::set($key,$userInfo);
|
||||
return json_decode($userInfo,true);
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
$userInfo = $this->adminUserRepository->findById($adminId)?->toArray() ?? null;
|
||||
$userInfo = $this->adminUserRepository->findById($adminId) ?? null;
|
||||
if (!$userInfo) return null;
|
||||
|
||||
Context::set($key, $userInfo);
|
||||
$this->redis->with()->set($key, json_encode($userInfo), 3600);
|
||||
$this->redis->with()->set($key, serialize($userInfo), 3600);
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
192
app/Trait/ClientIpTrait.php
Normal file
192
app/Trait/ClientIpTrait.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace App\Trait;
|
||||
|
||||
use App\Constants\Common\ClientIpRequestConstant;
|
||||
use App\Exception\ErrException;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||
use Symfony\Component\HttpFoundation\IpUtils;
|
||||
|
||||
trait ClientIpTrait
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
#[Inject]
|
||||
protected readonly RequestInterface $request;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected static array $trustedProxies = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private static int $trustedHeaderSet = -1;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private array $trustedValuesCache = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $isForwardedValid = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static bool $isTrustedRemoteAddr = false;
|
||||
|
||||
/**
|
||||
* 设置可信代理
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTrustedRemoteAddr(): bool
|
||||
{
|
||||
return self::$isTrustedRemoteAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回客户端IP地址。
|
||||
* 在返回的数组中,最可信的IP地址排在第一位,最不可信的IP地址排在最后。“真正的”客户端IP地址是最后一个,但这也是最不可信的一个。可信代理被剥离。
|
||||
* @return array
|
||||
*/
|
||||
public function getClientIp(): array
|
||||
{
|
||||
$ip = $this->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];
|
||||
}
|
||||
}
|
||||
25
app/Trait/ParserRouterTrait.php
Normal file
25
app/Trait/ParserRouterTrait.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Trait;
|
||||
|
||||
trait ParserRouterTrait
|
||||
{
|
||||
/**
|
||||
* @param $callback
|
||||
* @return array|string[]|null
|
||||
*/
|
||||
final protected function parse($callback): ?array
|
||||
{
|
||||
if (is_array($callback) && count($callback) == 2) return $callback;
|
||||
|
||||
if (is_string($callback)) {
|
||||
if (str_contains($callback, '@')) $explode = explode('@', $callback);
|
||||
|
||||
if (str_contains($callback, '::')) $explode = explode('::', $callback);
|
||||
|
||||
if (isset($explode) && count($explode) === 2) return $explode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user