Files
saasshop/app/Support/BackUrl.php

113 lines
3.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Support;
class BackUrl
{
/**
* back 参数安全护栏(用于 Blade 中 `{!! !!}` 输出的 href 场景):
* - 仅允许站内相对路径(/ 开头)
* - 拒绝引号/尖括号(防属性注入/XSS
* - 拒绝 nested back=(防 URL 膨胀/绕过)
*/
public static function sanitizeForLinks(string $incomingBack): string
{
$incomingBack = (string) $incomingBack;
if ($incomingBack === '') {
return '';
}
if (!str_starts_with($incomingBack, '/')) {
return '';
}
if (preg_match('/["\'<>]/', $incomingBack)) {
return '';
}
// 拒绝 back 自身再包含 back=(避免无限嵌套导致 URL 膨胀,且容易绕过页面侧护栏)
if (preg_match('/(?:^|[?&])back=/', $incomingBack)) {
return '';
}
return $incomingBack;
}
/**
* 安全版“保留当前 query 并覆盖字段”的站内相对链接构造器。
*
* 典型用途:列表页里的各种「统计卡/治理入口/快捷链接」需要:
* - 保留当前筛选条件
* - 覆盖指定字段
* - 并且 back 只能保留通过 sanitizeForLinks 的安全值(否则移除)
*/
public static function currentPathWithQuery(array $overrides = [], string $safeBackForLinks = ''): string
{
$q = request()->query();
if ($safeBackForLinks !== '') {
$q['back'] = $safeBackForLinks;
} else {
unset($q['back']);
}
foreach ($overrides as $k => $v) {
if ($v === null) {
unset($q[$k]);
} else {
$q[$k] = $v;
}
}
$url = '/' . ltrim(request()->path(), '/');
if (count($q) > 0) {
$url .= '?' . \Illuminate\Support\Arr::query($q);
}
return $url;
}
/**
* 当前路径下的“快捷筛选”链接构造器:
* - 仅保留指定上下文键(例如 merchant_id/plan_id/keyword/lead_id 等)
* - 覆盖 overridesnull 表示移除)
* - 强制清空 page避免落到空页
* - back 仅保留安全值(由调用方传入 sanitizeForLinks 产物)
*/
public static function currentPathQuickFilter(array $contextKeys, array $overrides = [], string $safeBackForLinks = ''): string
{
$path = '/' . ltrim(request()->path(), '/');
$contextMap = [];
foreach ($contextKeys as $k) {
$contextMap[(string) $k] = 1;
}
$q = array_intersect_key(request()->query(), $contextMap);
if ($safeBackForLinks !== '') {
$q['back'] = $safeBackForLinks;
} else {
unset($q['back']);
}
// 快捷筛选不应继承分页
unset($q['page']);
foreach ($overrides as $k => $v) {
if ($v === null) {
unset($q[$k]);
continue;
}
$q[$k] = $v;
}
if (count($q) === 0) {
return $path;
}
return $path . '?' . \Illuminate\Support\Arr::query($q);
}
}