From 5a1d9df9760270c4ab0886d038626229471c4b9a Mon Sep 17 00:00:00 2001 From: "iVampireSP.com" Date: Fri, 17 Feb 2023 20:58:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20=E8=B7=A8=E8=B6=8A=20?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=89=80=E6=9C=89=20CORS=20=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=20URL=20=E6=9D=A5=E8=AE=BE=E7=BD=AE=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 2 - .../Public/AuthRequestController.php | 3 ++ app/Http/Controllers/Web/AuthController.php | 42 +++++++++++++---- app/Http/Kernel.php | 7 +-- app/Http/Middleware/TrustedDomain.php | 47 +++++++++++++++++++ app/Rules/Domain.php | 31 ++++++++++++ config/cors.php | 8 +--- resources/views/auth/request.blade.php | 27 +++++++---- resources/views/index.blade.php | 7 +++ 9 files changed, 142 insertions(+), 32 deletions(-) create mode 100644 app/Http/Middleware/TrustedDomain.php create mode 100644 app/Rules/Domain.php diff --git a/.env.example b/.env.example index a37b484..5e47bfe 100644 --- a/.env.example +++ b/.env.example @@ -98,8 +98,6 @@ USER_GROUP_BIRTHDAY=1 DASHBOARD_BASE_URL=https://dash.laecloud.com DASHBOARD_BIRTHDAY_PATH=/stars -CORS_ORIGINS=${DASHBOARD_BASE_URL},https://web.laecloud.com,http://localhost:5173 - # 可信代理,用于获取真实 IP。多个 IP 用逗号分隔。 TRUSTED_PROXIES= diff --git a/app/Http/Controllers/Public/AuthRequestController.php b/app/Http/Controllers/Public/AuthRequestController.php index 47d9349..7ed4e78 100644 --- a/app/Http/Controllers/Public/AuthRequestController.php +++ b/app/Http/Controllers/Public/AuthRequestController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Public; use App\Http\Controllers\Controller; +use App\Rules\Domain; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; @@ -15,6 +16,7 @@ public function store(Request $request): JsonResponse $request->validate([ 'description' => 'required|string|max:255', 'require_token' => 'nullable|boolean', + 'abilities' => 'nullable|array|max:255', ]); $token = Str::random(128); @@ -24,6 +26,7 @@ public function store(Request $request): JsonResponse 'description' => $request->input('description'), 'token' => $token, 'require_token' => $request->input('require_token', false), + 'abilities' => $request->input('abilities'), ], ]; diff --git a/app/Http/Controllers/Web/AuthController.php b/app/Http/Controllers/Web/AuthController.php index d9ea0dc..181094a 100644 --- a/app/Http/Controllers/Web/AuthController.php +++ b/app/Http/Controllers/Web/AuthController.php @@ -6,14 +6,15 @@ use App\Http\Controllers\Controller; use App\Notifications\User\UserNotification; -use function back; -use function config; +use App\Rules\Domain; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\View\View; +use function back; +use function config; use function redirect; use function session; use function view; @@ -33,13 +34,17 @@ public function index(Request $request): View|RedirectResponse $dashboardHost = parse_url(config('settings.dashboard.base_url'), PHP_URL_HOST); if ($callbackHost === $dashboardHost) { - if (! $request->user('web')->isRealNamed()) { + if (!$request->user('web')->isRealNamed()) { return redirect()->route('real_name.create')->with('status', '重定向已被打断,需要先实人认证。'); } - $token = $request->user()->createToken('Dashboard')->plainTextToken; + $requestHost = parse_url($request->header('referer'), PHP_URL_HOST); - return redirect($callback.'?token='.$token); + $token = $request->user()->createToken('Dashboard', [ + 'domain-access:' . $requestHost, + ])->plainTextToken; + + return redirect($callback . '?token=' . $token); } return redirect()->route('confirm_redirect'); @@ -80,9 +85,24 @@ public function newToken(Request $request): RedirectResponse { $request->validate([ 'name' => 'required|string|max:255', + 'domain' => ['nullable', 'string', 'max:255', new Domain], ]); - $token = $request->user()->createToken($request->input('name')); + $abilities = []; + + if ($request->has('domain')) { + // 检测是不是一个合格的域名 + if (!preg_match('/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/', $request->input('domain'))) { + return back()->with('error', '域名格式不正确。'); + } + + $abilities = ['domain-access:' . $request->input('domain')]; + } + + $token = $request->user()->createToken( + $request->input('name'), + $abilities + ); return back()->with('token', $token->plainTextToken); } @@ -115,7 +135,7 @@ public function exitSudo(): RedirectResponse public function showAuthRequest($token): View|RedirectResponse { - $data = Cache::get('auth_request:'.$token); + $data = Cache::get('auth_request:' . $token); if (empty($data)) { return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。'); @@ -139,7 +159,7 @@ public function storeAuthRequest(Request $request): RedirectResponse 'token' => 'required|string|max:128', ]); - $data = Cache::get('auth_request:'.$request->input('token')); + $data = Cache::get('auth_request:' . $request->input('token')); if (empty($data)) { return back()->with('error', '登录请求的 Token 不存在或已过期。'); @@ -157,11 +177,13 @@ public function storeAuthRequest(Request $request): RedirectResponse 'real_name_verified_at', ]); + $abilities = $data['meta']['abilities'] ?? ['*']; + if (isset($data['meta']['require_token']) && $data['meta']['require_token']) { - $data['token'] = $user->createToken($data['meta']['description'] ?? Carbon::now()->toDateString())->plainTextToken; + $data['token'] = $user->createToken($data['meta']['description'] ?? Carbon::now()->toDateString(), $abilities)->plainTextToken; } - Cache::put('auth_request:'.$request->input('token'), $data, 60); + Cache::put('auth_request:' . $request->input('token'), $data, 60); return redirect()->route('index')->with('success', '登录请求已确认。'); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index a847647..bf5b7f1 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -13,11 +13,11 @@ use App\Http\Middleware\RedirectIfAuthenticated; use App\Http\Middleware\ReportRequestToCluster; use App\Http\Middleware\TrimStrings; +use App\Http\Middleware\TrustedDomain; use App\Http\Middleware\TrustProxies; use App\Http\Middleware\ValidateSignature; use App\Http\Middleware\ValidateUserIfBanned; use App\Http\Middleware\VerifyCsrfToken; -use Fruitcake\Cors\HandleCors; use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; use Illuminate\Auth\Middleware\Authorize; use Illuminate\Auth\Middleware\EnsureEmailIsVerified; @@ -26,6 +26,7 @@ use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; use Illuminate\Foundation\Http\Middleware\ValidatePostSize; +use Illuminate\Http\Middleware\HandleCors; use Illuminate\Http\Middleware\SetCacheHeaders; use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Routing\Middleware\ThrottleRequests; @@ -45,12 +46,11 @@ class Kernel extends HttpKernel protected $middleware = [ // \App\Http\Middleware\TrustHosts::class, TrustProxies::class, - // \Illuminate\Http\Middleware\HandleCors::class, + HandleCors::class, PreventRequestsDuringMaintenance::class, ValidatePostSize::class, TrimStrings::class, ConvertEmptyStringsToNull::class, - HandleCors::class, AddHeaders::class, ]; @@ -72,6 +72,7 @@ class Kernel extends HttpKernel 'api' => [ JsonRequest::class, + TrustedDomain::class, // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', SubstituteBindings::class, diff --git a/app/Http/Middleware/TrustedDomain.php b/app/Http/Middleware/TrustedDomain.php new file mode 100644 index 0000000..fb64fbe --- /dev/null +++ b/app/Http/Middleware/TrustedDomain.php @@ -0,0 +1,47 @@ +user('sanctum'); + + if (!$user) { + return $next($request); + } + + // 获取请求的域名 + $requestHost = parse_url($request->header('referer'), PHP_URL_HOST); + + if ($requestHost) { + // 获取当前域名 + $currentHost = parse_url(config('app.url'), PHP_URL_HOST); + + // 如果请求的域名和当前域名相同,则直接放行 + if ($requestHost === $currentHost) { + return $next($request); + } + + return $user->tokenCan('domain-access:' . $requestHost) ? $next($request) : response()->json([ + 'message' => 'Token 无权访问此域名。', + ], 401); + } + + return $next($request); + } +} diff --git a/app/Rules/Domain.php b/app/Rules/Domain.php new file mode 100644 index 0000000..e79763a --- /dev/null +++ b/app/Rules/Domain.php @@ -0,0 +1,31 @@ + ['*'], - 'allowed_origins' => $cors_origin, + 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], diff --git a/resources/views/auth/request.blade.php b/resources/views/auth/request.blade.php index 64c5904..796f5f7 100644 --- a/resources/views/auth/request.blade.php +++ b/resources/views/auth/request.blade.php @@ -7,7 +7,6 @@

@if (isset($data['module']) && !is_null($data['module'])) - ) 模块:{{ $data['module']['name'] }} @elseif (isset($data['applications']) && !is_null($data['application'])) 应用程序:{{ $data['application']['name'] }} @@ -23,18 +22,26 @@

{{ $data['meta']['description'] }}


-

- 在您同意后,您的 ID, UUID, 昵称, 邮件信息 和 实人认证成功的时间(不包含个人信息), 余额, - 用户组 ID 将会被发送给它们。 - @if ($data['meta']['require_token']) -
- 你的 Token 将会新建一个,并发送给它们。 - @endif -

+ 在您同意后,您的 ID, UUID, 昵称, 邮件信息 和 实人认证成功的时间(不包含个人信息), + 余额, + 用户组 ID 将会被发送给它们。 + @if ($data['meta']['require_token']) +
+ 你的 Token 将会新建一个,并发送给它们。 + @endif + + @if (isset($data['meta']['abilities'])) +
+ 权限列表: + @foreach($data['meta']['abilities'] as $ability) + {{ $ability }} + @endforeach +
+ @endif @auth('web') -
+ @csrf diff --git a/resources/views/index.blade.php b/resources/views/index.blade.php index ac6c329..82825a9 100644 --- a/resources/views/index.blade.php +++ b/resources/views/index.blade.php @@ -45,12 +45,19 @@ @csrf +
+
+ + +
+