改进 跨越
允许所有 CORS 根据 URL 来设置权限
This commit is contained in:
parent
cc41963734
commit
5a1d9df976
@ -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=
|
||||
|
||||
|
@ -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'),
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -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', '登录请求已确认。');
|
||||
}
|
||||
|
@ -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,
|
||||
|
47
app/Http/Middleware/TrustedDomain.php
Normal file
47
app/Http/Middleware/TrustedDomain.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class TrustedDomain
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure(Request): (Response|RedirectResponse) $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$user = $request->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);
|
||||
}
|
||||
}
|
31
app/Rules/Domain.php
Normal file
31
app/Rules/Domain.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class Domain implements Rule
|
||||
{
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function passes($attribute, $value): bool
|
||||
{
|
||||
// 验证域名是否合法
|
||||
return preg_match('/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function message(): string
|
||||
{
|
||||
return '域名格式不正确。';
|
||||
}
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
<?php
|
||||
|
||||
$cors_origin = explode(',', env('CORS_ORIGINS'));
|
||||
|
||||
if (env('APP_ENV') === 'local') {
|
||||
$cors_origin = ['*'];
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@ -25,7 +19,7 @@
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => $cors_origin,
|
||||
'allowed_origins' => ['*'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
<h3>
|
||||
<code>
|
||||
@if (isset($data['module']) && !is_null($data['module']))
|
||||
)
|
||||
<span>模块:{{ $data['module']['name'] }}</span>
|
||||
@elseif (isset($data['applications']) && !is_null($data['application']))
|
||||
<span>应用程序:{{ $data['application']['name'] }}</span>
|
||||
@ -23,18 +22,26 @@
|
||||
<p>{{ $data['meta']['description'] }}</p>
|
||||
|
||||
<br/>
|
||||
<p>
|
||||
在您同意后,您的 <b>ID</b>, <b>UUID</b>, <b>昵称</b>, <b>邮件信息 和 实人认证成功的时间(不包含个人信息)</b>, <b>余额</b>,
|
||||
<b>用户组 ID</b> 将会被发送给它们。
|
||||
@if ($data['meta']['require_token'])
|
||||
<br />
|
||||
你的 <b>Token</b> 将会新建一个,并发送给它们。
|
||||
@endif
|
||||
</p>
|
||||
|
||||
在您同意后,您的 <b>ID</b>, <b>UUID</b>, <b>昵称</b>, <b>邮件信息 和 实人认证成功的时间(不包含个人信息)</b>,
|
||||
<b>余额</b>,
|
||||
<b>用户组 ID</b> 将会被发送给它们。
|
||||
@if ($data['meta']['require_token'])
|
||||
<br/>
|
||||
你的 <b>Token</b> 将会新建一个,并发送给它们。
|
||||
@endif
|
||||
|
||||
@if (isset($data['meta']['abilities']))
|
||||
<div>
|
||||
权限列表:
|
||||
@foreach($data['meta']['abilities'] as $ability)
|
||||
<b>{{ $ability }}</b>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@auth('web')
|
||||
<form method="POST" action="{{ route('auth_request.store') }}">
|
||||
<form method="POST" action="{{ route('auth_request.store') }}" class="mt-3">
|
||||
@csrf
|
||||
<input type="hidden" name="token" value="{{ $data['meta']['token'] }}">
|
||||
<button type="submit" class="btn btn-primary">同意</button>
|
||||
|
@ -45,12 +45,19 @@
|
||||
|
||||
<form action="{{ route('token.new') }}" name="newToken" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="form-floating mb-2">
|
||||
<input type="text" class="form-control" placeholder="Token 名称"
|
||||
aria-label="密钥名称" name="name" required maxlength="25">
|
||||
<label>Token 名称</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-2">
|
||||
<input type="text" class="form-control" placeholder="授权的域名"
|
||||
aria-label="授权的域名" name="domain" maxlength="255">
|
||||
<label>授权的域名</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary visually-hidden">
|
||||
创建
|
||||
</button>
|
||||
|
Loading…
Reference in New Issue
Block a user