mirror of
https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git
synced 2025-12-25 17:07:49 +08:00
fix : update path And request
This commit is contained in:
10
app/Common/Interface/CheckTokenInterface.php
Normal file
10
app/Common/Interface/CheckTokenInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Interface;
|
||||
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
|
||||
interface CheckTokenInterface
|
||||
{
|
||||
public function checkJwt(UnencryptedToken $token): void;
|
||||
}
|
||||
24
app/Common/Interface/JwtInterface.php
Normal file
24
app/Common/Interface/JwtInterface.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Interface;
|
||||
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
|
||||
interface JwtInterface
|
||||
{
|
||||
public function builderAccessToken(string $sub, ?\Closure $callable = null): UnencryptedToken;
|
||||
|
||||
public function builderRefreshToken(string $sub, ?\Closure $callable = null): UnencryptedToken;
|
||||
|
||||
public function parserAccessToken(string $accessToken): UnencryptedToken;
|
||||
|
||||
public function parserRefreshToken(string $refreshToken): UnencryptedToken;
|
||||
|
||||
public function addBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function hasBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function removeBlackList(UnencryptedToken $token): bool;
|
||||
|
||||
public function getConfig(string $key, mixed $default = null): mixed;
|
||||
}
|
||||
80
app/Common/Repository/AdminMenuRepository.php
Normal file
80
app/Common/Repository/AdminMenuRepository.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Constants\Model\AdminUser\AdminMenuStatusCode;
|
||||
use App\Model\AdminMenu;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Collection\Collection;
|
||||
use Hyperf\Database\Model\Builder;
|
||||
|
||||
final class AdminMenuRepository extends BaseRepository
|
||||
{
|
||||
/**
|
||||
* @param AdminMenu $model
|
||||
*/
|
||||
public function __construct(protected readonly AdminMenu $model) {}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function enablePageOrderBy(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return Collection
|
||||
*/
|
||||
public function list(array $params = []): Collection
|
||||
{
|
||||
return $this->perQuery($this->getQuery(), $params)->orderBy('sort')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
* @param array $params
|
||||
* @return Builder
|
||||
*/
|
||||
public function handleSearch(Builder $query, array $params): Builder
|
||||
{
|
||||
$whereInName = static function (Builder $query, array|string $code) {
|
||||
$query->whereIn('name', Arr::wrap($code));
|
||||
};
|
||||
return $query
|
||||
->when(Arr::get($params, 'sortable'), static function (Builder $query, array $sortable) {
|
||||
$query->orderBy(key($sortable), current($sortable));
|
||||
})
|
||||
->when(Arr::get($params, 'code'), $whereInName)
|
||||
->when(Arr::get($params, 'name'), $whereInName)
|
||||
->when(Arr::get($params, 'children'), static function (Builder $query) {
|
||||
$query->with('children');
|
||||
})->when(Arr::get($params, 'status'), static function (Builder $query, AdminMenuStatusCode $status) {
|
||||
$query->where('status', $status);
|
||||
})
|
||||
->when(Arr::has($params, 'parent_id'), static function (Builder $query) use ($params) {
|
||||
$query->where('parent_id', Arr::get($params, 'parent_id'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder[]|\Hyperf\Database\Model\Collection
|
||||
*/
|
||||
public function allTree(): \Hyperf\Database\Model\Collection|array
|
||||
{
|
||||
return $this->model
|
||||
->newQuery()
|
||||
->where('parent_id', 0)
|
||||
->with('children')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
18
app/Common/Repository/AdminRoleRepository.php
Normal file
18
app/Common/Repository/AdminRoleRepository.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Model\AdminRole;
|
||||
|
||||
final class AdminRoleRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(protected readonly AdminRole $model) {}
|
||||
}
|
||||
57
app/Common/Repository/AdminUserLoginLogRepository.php
Normal file
57
app/Common/Repository/AdminUserLoginLogRepository.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Model\AdminUserLoginLog;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Database\Model\Builder;
|
||||
|
||||
final class AdminUserLoginLogRepository extends BaseRepository
|
||||
{
|
||||
/**
|
||||
* @param AdminUserLoginLog $model
|
||||
*/
|
||||
public function __construct(protected readonly AdminUserLoginLog $model) {}
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
* @param array $params
|
||||
* @return Builder
|
||||
*/
|
||||
public function handleSearch(Builder $query, array $params): Builder
|
||||
{
|
||||
return $query
|
||||
->when(Arr::get($params, 'username'), static function (Builder $query, $username) {
|
||||
$query->where('username', $username);
|
||||
})
|
||||
->when(Arr::get($params, 'ip'), static function (Builder $query, $ip) {
|
||||
$query->where('ip', $ip);
|
||||
})
|
||||
->when(Arr::get($params, 'os'), static function (Builder $query, $os) {
|
||||
$query->where('os', $os);
|
||||
})
|
||||
->when(Arr::get($params, 'browser'), static function (Builder $query, $browser) {
|
||||
$query->where('browser', $browser);
|
||||
})
|
||||
->when(Arr::get($params, 'status'), static function (Builder $query, $status) {
|
||||
$query->where('status', $status);
|
||||
})
|
||||
->when(Arr::get($params, 'message'), static function (Builder $query, $message) {
|
||||
$query->where('message', $message);
|
||||
})
|
||||
->when(Arr::get($params, 'login_time'), static function (Builder $query, $login_time) {
|
||||
$query->whereBetween('login_time', $login_time);
|
||||
})
|
||||
->when(Arr::get($params, 'remark'), static function (Builder $query, $remark) {
|
||||
$query->where('remark', $remark);
|
||||
});
|
||||
}
|
||||
}
|
||||
18
app/Common/Repository/AdminUserOperationLogRepository.php
Normal file
18
app/Common/Repository/AdminUserOperationLogRepository.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Model\AdminUserOperationLog;
|
||||
|
||||
final class AdminUserOperationLogRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(protected readonly AdminUserOperationLog $model) {}
|
||||
}
|
||||
81
app/Common/Repository/AdminUserRepository.php
Normal file
81
app/Common/Repository/AdminUserRepository.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Model\AdminUser;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Database\Model\Builder;
|
||||
|
||||
/**
|
||||
* Class AdminUserRepository
|
||||
* @extends BaseRepository<AdminUser>
|
||||
*/
|
||||
final class AdminUserRepository extends BaseRepository
|
||||
{
|
||||
public function __construct(protected readonly AdminUser $model) {}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @return AdminUser|null
|
||||
*/
|
||||
public function findByUserName(string $username): AdminUser|null
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->model->newQuery()
|
||||
->where('username', $username)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder $query
|
||||
* @param array $params
|
||||
* @return Builder
|
||||
*/
|
||||
public function handleSearch(Builder $query, array $params): Builder
|
||||
{
|
||||
return $query
|
||||
->when(Arr::get($params, 'unique_username'), static function (Builder $query, $uniqueUsername) {
|
||||
$query->where('username', $uniqueUsername);
|
||||
})
|
||||
->when(Arr::get($params, 'username'), static function (Builder $query, $username) {
|
||||
$query->where('username', 'like', '%' . $username . '%');
|
||||
})
|
||||
->when(Arr::get($params, 'phone'), static function (Builder $query, $phone) {
|
||||
$query->where('phone', $phone);
|
||||
})
|
||||
->when(Arr::get($params, 'email'), static function (Builder $query, $email) {
|
||||
$query->where('email', $email);
|
||||
})
|
||||
->when(Arr::exists($params, 'status'), static function (Builder $query) use ($params) {
|
||||
$query->where('status', Arr::get($params, 'status'));
|
||||
})
|
||||
->when(Arr::exists($params, 'user_type'), static function (Builder $query) use ($params) {
|
||||
$query->where('user_type', Arr::get($params, 'user_type'));
|
||||
})
|
||||
->when(Arr::exists($params, 'nickname'), static function (Builder $query) use ($params) {
|
||||
$query->where('nickname', 'like', '%' . Arr::get($params, 'nickname') . '%');
|
||||
})
|
||||
->when(Arr::exists($params, 'created_at'), static function (Builder $query) use ($params) {
|
||||
$query->whereBetween('created_at', [
|
||||
Arr::get($params, 'created_at')[0] . ' 00:00:00',
|
||||
Arr::get($params, 'created_at')[1] . ' 23:59:59',
|
||||
]);
|
||||
})
|
||||
->when(Arr::get($params, 'user_ids'), static function (Builder $query, $userIds) {
|
||||
$query->whereIn('id', $userIds);
|
||||
})
|
||||
->when(Arr::get($params, 'role_id'), static function (Builder $query, $roleId) {
|
||||
$query->whereHas('roles', static function (Builder $query) use ($roleId) {
|
||||
$query->where('role_id', $roleId);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
151
app/Common/Repository/BaseRepository.php
Normal file
151
app/Common/Repository/BaseRepository.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Repository;
|
||||
|
||||
use App\Common\Repository\Traits\BootTrait;
|
||||
use App\Common\Repository\Traits\RepositoryOrderByTrait;
|
||||
use Hyperf\Collection\Collection;
|
||||
use Hyperf\Contract\LengthAwarePaginatorInterface;
|
||||
use Hyperf\Database\Model\Builder;
|
||||
use Hyperf\Database\Model\Model;
|
||||
use Hyperf\DbConnection\Traits\HasContainer;
|
||||
use Hyperf\Paginator\AbstractPaginator;
|
||||
|
||||
/**
|
||||
* @template T of Model
|
||||
* @property Model $model
|
||||
*/
|
||||
abstract class BaseRepository
|
||||
{
|
||||
use BootTrait;
|
||||
use HasContainer;
|
||||
use RepositoryOrderByTrait;
|
||||
|
||||
public const string PER_PAGE_PARAM_NAME = 'per_page';
|
||||
public function handleSearch(Builder $query, array $params): Builder
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function handleItems(Collection $items): Collection
|
||||
{
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function handlePage(LengthAwarePaginatorInterface $paginator): array
|
||||
{
|
||||
if ($paginator instanceof AbstractPaginator) {
|
||||
$items = $paginator->getCollection();
|
||||
} else {
|
||||
$items = Collection::make($paginator->items());
|
||||
}
|
||||
$items = $this->handleItems($items);
|
||||
return [
|
||||
'list' => $items->toArray(),
|
||||
'total' => $paginator->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function list(array $params = []): Collection
|
||||
{
|
||||
return $this->handleItems($this->perQuery($this->getQuery(), $params)->get());
|
||||
}
|
||||
|
||||
public function count(array $params = []): int
|
||||
{
|
||||
return $this->perQuery($this->getQuery(), $params)->count();
|
||||
}
|
||||
|
||||
public function page(array $params = [], ?int $page = null, ?int $pageSize = null): array
|
||||
{
|
||||
$result = $this->perQuery($this->getQuery(), $params)->paginate(
|
||||
perPage: $pageSize,
|
||||
pageName: static::PER_PAGE_PARAM_NAME,
|
||||
page: $page,
|
||||
);
|
||||
return $this->handlePage($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Model
|
||||
*/
|
||||
public function create(array $data): mixed
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->getQuery()->create($data);
|
||||
}
|
||||
|
||||
public function updateById(mixed $id, array $data): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->first()?->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Model
|
||||
*/
|
||||
public function saveById(mixed $id, array $data): mixed
|
||||
{
|
||||
$model = $this->getQuery()->whereKey($id)->first();
|
||||
if ($model) {
|
||||
$model->fill($data)->save();
|
||||
return $model;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function deleteById(mixed $id): int
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->model::destroy($id);
|
||||
}
|
||||
|
||||
public function forceDeleteById(mixed $id): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Model
|
||||
*/
|
||||
public function findById(mixed $id): mixed
|
||||
{
|
||||
return $this->getQuery()->whereKey($id)->first();
|
||||
}
|
||||
|
||||
public function findByField(mixed $id, string $field): mixed
|
||||
{
|
||||
return $this->getQuery()->whereKey($id)->value($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Model
|
||||
*/
|
||||
public function findByFilter(array $params): mixed
|
||||
{
|
||||
return $this->perQuery($this->getQuery(), $params)->first();
|
||||
}
|
||||
|
||||
public function perQuery(Builder $query, array $params): Builder
|
||||
{
|
||||
$this->startBoot($query, $params);
|
||||
return $this->handleSearch($query, $params);
|
||||
}
|
||||
|
||||
public function getQuery(): Builder
|
||||
{
|
||||
return $this->model->newQuery();
|
||||
}
|
||||
|
||||
public function existsById(mixed $id): bool
|
||||
{
|
||||
return (bool) $this->getQuery()->whereKey($id)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Model
|
||||
*/
|
||||
public function getModel(): Model
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
}
|
||||
20
app/Common/Repository/Traits/BootTrait.php
Normal file
20
app/Common/Repository/Traits/BootTrait.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Repository\Traits;
|
||||
|
||||
use function Hyperf\Support\class_basename;
|
||||
use function Hyperf\Support\class_uses_recursive;
|
||||
|
||||
trait BootTrait
|
||||
{
|
||||
protected function startBoot(...$params): void
|
||||
{
|
||||
$traits = class_uses_recursive(static::class);
|
||||
foreach ($traits as $trait) {
|
||||
$method = 'boot' . class_basename($trait);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->{$method}(...$params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/Common/Repository/Traits/RepositoryOrderByTrait.php
Normal file
38
app/Common/Repository/Traits/RepositoryOrderByTrait.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Repository\Traits;
|
||||
|
||||
use Hyperf\Database\Model\Builder;
|
||||
|
||||
trait RepositoryOrderByTrait
|
||||
{
|
||||
public function handleOrderBy(Builder $query, $params): Builder
|
||||
{
|
||||
if ($this->enablePageOrderBy()) {
|
||||
$orderByField = $params[$this->getOrderByParamName()] ?? $query->getModel()->getKeyName();
|
||||
$orderByDirection = $params[$this->getOrderByDirectionParamName()] ?? 'desc';
|
||||
$query->orderBy($orderByField, $orderByDirection);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function bootRepositoryOrderByTrait(Builder $query, array $params): void
|
||||
{
|
||||
$this->handleOrderBy($query, $params);
|
||||
}
|
||||
|
||||
protected function getOrderByParamName(): string
|
||||
{
|
||||
return 'order_by';
|
||||
}
|
||||
|
||||
protected function getOrderByDirectionParamName(): string
|
||||
{
|
||||
return 'order_by_direction';
|
||||
}
|
||||
|
||||
protected function enablePageOrderBy(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
52
app/Common/Trait/AdminUserTrait.php
Normal file
52
app/Common/Trait/AdminUserTrait.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Trait;
|
||||
|
||||
use App\Cache\Redis\RedisCache;
|
||||
use App\Cache\Redis\RedisKey;
|
||||
use App\Common\Repository\AdminUserRepository;
|
||||
use App\Model\AdminUser;
|
||||
use Hyperf\Context\Context;
|
||||
use Hyperf\Database\Model\Model;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
trait AdminUserTrait
|
||||
{
|
||||
/**
|
||||
* @var RedisCache
|
||||
*/
|
||||
#[Inject]
|
||||
protected RedisCache $redis;
|
||||
|
||||
/**
|
||||
* @var AdminUserRepository
|
||||
*/
|
||||
#[Inject]
|
||||
protected AdminUserRepository $adminUserRepository;
|
||||
|
||||
/**
|
||||
* @param int $adminId
|
||||
* @return AdminUser|Model|mixed|string|null
|
||||
*/
|
||||
public function getAdminUserInfo(int $adminId): mixed
|
||||
{
|
||||
$key = RedisKey::getAdminUserInfoKey($adminId);
|
||||
if (Context::has($key)) {
|
||||
return Context::get($key,false);
|
||||
}
|
||||
|
||||
if ($this->redis->with()->exists($key)) {
|
||||
$userInfo = unserialize($this->redis->with()->get($key));
|
||||
Context::set($key,$userInfo);
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
$userInfo = $this->adminUserRepository->findById($adminId) ?? null;
|
||||
if (!$userInfo) return null;
|
||||
|
||||
Context::set($key, $userInfo);
|
||||
$this->redis->with()->set($key, serialize($userInfo), 3600);
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
}
|
||||
192
app/Common/Trait/ClientIpTrait.php
Normal file
192
app/Common/Trait/ClientIpTrait.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\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];
|
||||
}
|
||||
}
|
||||
34
app/Common/Trait/ClientOsTrait.php
Normal file
34
app/Common/Trait/ClientOsTrait.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Trait;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
|
||||
trait ClientOsTrait
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
#[Inject]
|
||||
protected readonly RequestInterface $request;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClientOs(): string
|
||||
{
|
||||
$userAgent = $this->request->header('user-agent');
|
||||
|
||||
if (empty($userAgent)) return 'Unknown';
|
||||
|
||||
return match (true) {
|
||||
preg_match('/win/i', $userAgent) => 'Windows',
|
||||
preg_match('/mac/i', $userAgent) => 'MAC',
|
||||
preg_match('/linux/i', $userAgent) => 'Linux',
|
||||
preg_match('/unix/i', $userAgent) => 'Unix',
|
||||
preg_match('/bsd/i', $userAgent) => 'BSD',
|
||||
default => 'Other',
|
||||
};
|
||||
}
|
||||
}
|
||||
47
app/Common/Trait/HttpMethodTrait.php
Normal file
47
app/Common/Trait/HttpMethodTrait.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\Trait;
|
||||
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Hyperf\HttpServer\Contract\RequestInterface;
|
||||
|
||||
trait HttpMethodTrait
|
||||
{
|
||||
/**
|
||||
* @var RequestInterface
|
||||
*/
|
||||
#[Inject]
|
||||
protected readonly RequestInterface $request;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCreate(): bool
|
||||
{
|
||||
return $this->request->isMethod('POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isUpdate(): bool
|
||||
{
|
||||
return $this->request->isMethod('PATCH') || $this->request->isMethod('PUT');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDelete(): bool
|
||||
{
|
||||
return $this->request->isMethod('DELETE');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSearch(): bool
|
||||
{
|
||||
return $this->request->isMethod('GET');
|
||||
}
|
||||
}
|
||||
25
app/Common/Trait/ParserRouterTrait.php
Normal file
25
app/Common/Trait/ParserRouterTrait.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Common\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