改进 跨越
允许所有 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_BASE_URL=https://dash.laecloud.com
|
||||||
DASHBOARD_BIRTHDAY_PATH=/stars
|
DASHBOARD_BIRTHDAY_PATH=/stars
|
||||||
|
|
||||||
CORS_ORIGINS=${DASHBOARD_BASE_URL},https://web.laecloud.com,http://localhost:5173
|
|
||||||
|
|
||||||
# 可信代理,用于获取真实 IP。多个 IP 用逗号分隔。
|
# 可信代理,用于获取真实 IP。多个 IP 用逗号分隔。
|
||||||
TRUSTED_PROXIES=
|
TRUSTED_PROXIES=
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Public;
|
namespace App\Http\Controllers\Public;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Rules\Domain;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
@ -15,6 +16,7 @@ public function store(Request $request): JsonResponse
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'description' => 'required|string|max:255',
|
'description' => 'required|string|max:255',
|
||||||
'require_token' => 'nullable|boolean',
|
'require_token' => 'nullable|boolean',
|
||||||
|
'abilities' => 'nullable|array|max:255',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$token = Str::random(128);
|
$token = Str::random(128);
|
||||||
@ -24,6 +26,7 @@ public function store(Request $request): JsonResponse
|
|||||||
'description' => $request->input('description'),
|
'description' => $request->input('description'),
|
||||||
'token' => $token,
|
'token' => $token,
|
||||||
'require_token' => $request->input('require_token', false),
|
'require_token' => $request->input('require_token', false),
|
||||||
|
'abilities' => $request->input('abilities'),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -6,14 +6,15 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Notifications\User\UserNotification;
|
use App\Notifications\User\UserNotification;
|
||||||
use function back;
|
use App\Rules\Domain;
|
||||||
use function config;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
use function back;
|
||||||
|
use function config;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
use function session;
|
use function session;
|
||||||
use function view;
|
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);
|
$dashboardHost = parse_url(config('settings.dashboard.base_url'), PHP_URL_HOST);
|
||||||
|
|
||||||
if ($callbackHost === $dashboardHost) {
|
if ($callbackHost === $dashboardHost) {
|
||||||
if (! $request->user('web')->isRealNamed()) {
|
if (!$request->user('web')->isRealNamed()) {
|
||||||
return redirect()->route('real_name.create')->with('status', '重定向已被打断,需要先实人认证。');
|
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');
|
return redirect()->route('confirm_redirect');
|
||||||
@ -80,9 +85,24 @@ public function newToken(Request $request): RedirectResponse
|
|||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'name' => 'required|string|max:255',
|
'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);
|
return back()->with('token', $token->plainTextToken);
|
||||||
}
|
}
|
||||||
@ -115,7 +135,7 @@ public function exitSudo(): RedirectResponse
|
|||||||
|
|
||||||
public function showAuthRequest($token): View|RedirectResponse
|
public function showAuthRequest($token): View|RedirectResponse
|
||||||
{
|
{
|
||||||
$data = Cache::get('auth_request:'.$token);
|
$data = Cache::get('auth_request:' . $token);
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。');
|
return redirect()->route('index')->with('error', '登录请求的 Token 不存在或已过期。');
|
||||||
@ -139,7 +159,7 @@ public function storeAuthRequest(Request $request): RedirectResponse
|
|||||||
'token' => 'required|string|max:128',
|
'token' => 'required|string|max:128',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data = Cache::get('auth_request:'.$request->input('token'));
|
$data = Cache::get('auth_request:' . $request->input('token'));
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return back()->with('error', '登录请求的 Token 不存在或已过期。');
|
return back()->with('error', '登录请求的 Token 不存在或已过期。');
|
||||||
@ -157,11 +177,13 @@ public function storeAuthRequest(Request $request): RedirectResponse
|
|||||||
'real_name_verified_at',
|
'real_name_verified_at',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$abilities = $data['meta']['abilities'] ?? ['*'];
|
||||||
|
|
||||||
if (isset($data['meta']['require_token']) && $data['meta']['require_token']) {
|
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', '登录请求已确认。');
|
return redirect()->route('index')->with('success', '登录请求已确认。');
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||||
use App\Http\Middleware\ReportRequestToCluster;
|
use App\Http\Middleware\ReportRequestToCluster;
|
||||||
use App\Http\Middleware\TrimStrings;
|
use App\Http\Middleware\TrimStrings;
|
||||||
|
use App\Http\Middleware\TrustedDomain;
|
||||||
use App\Http\Middleware\TrustProxies;
|
use App\Http\Middleware\TrustProxies;
|
||||||
use App\Http\Middleware\ValidateSignature;
|
use App\Http\Middleware\ValidateSignature;
|
||||||
use App\Http\Middleware\ValidateUserIfBanned;
|
use App\Http\Middleware\ValidateUserIfBanned;
|
||||||
use App\Http\Middleware\VerifyCsrfToken;
|
use App\Http\Middleware\VerifyCsrfToken;
|
||||||
use Fruitcake\Cors\HandleCors;
|
|
||||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||||
use Illuminate\Auth\Middleware\Authorize;
|
use Illuminate\Auth\Middleware\Authorize;
|
||||||
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
|
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
|
||||||
@ -26,6 +26,7 @@
|
|||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||||
|
use Illuminate\Http\Middleware\HandleCors;
|
||||||
use Illuminate\Http\Middleware\SetCacheHeaders;
|
use Illuminate\Http\Middleware\SetCacheHeaders;
|
||||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
@ -45,12 +46,11 @@ class Kernel extends HttpKernel
|
|||||||
protected $middleware = [
|
protected $middleware = [
|
||||||
// \App\Http\Middleware\TrustHosts::class,
|
// \App\Http\Middleware\TrustHosts::class,
|
||||||
TrustProxies::class,
|
TrustProxies::class,
|
||||||
// \Illuminate\Http\Middleware\HandleCors::class,
|
HandleCors::class,
|
||||||
PreventRequestsDuringMaintenance::class,
|
PreventRequestsDuringMaintenance::class,
|
||||||
ValidatePostSize::class,
|
ValidatePostSize::class,
|
||||||
TrimStrings::class,
|
TrimStrings::class,
|
||||||
ConvertEmptyStringsToNull::class,
|
ConvertEmptyStringsToNull::class,
|
||||||
HandleCors::class,
|
|
||||||
AddHeaders::class,
|
AddHeaders::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -72,6 +72,7 @@ class Kernel extends HttpKernel
|
|||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
JsonRequest::class,
|
JsonRequest::class,
|
||||||
|
TrustedDomain::class,
|
||||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||||
'throttle:api',
|
'throttle:api',
|
||||||
SubstituteBindings::class,
|
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
|
<?php
|
||||||
|
|
||||||
$cors_origin = explode(',', env('CORS_ORIGINS'));
|
|
||||||
|
|
||||||
if (env('APP_ENV') === 'local') {
|
|
||||||
$cors_origin = ['*'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -25,7 +19,7 @@
|
|||||||
|
|
||||||
'allowed_methods' => ['*'],
|
'allowed_methods' => ['*'],
|
||||||
|
|
||||||
'allowed_origins' => $cors_origin,
|
'allowed_origins' => ['*'],
|
||||||
|
|
||||||
'allowed_origins_patterns' => [],
|
'allowed_origins_patterns' => [],
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
<h3>
|
<h3>
|
||||||
<code>
|
<code>
|
||||||
@if (isset($data['module']) && !is_null($data['module']))
|
@if (isset($data['module']) && !is_null($data['module']))
|
||||||
)
|
|
||||||
<span>模块:{{ $data['module']['name'] }}</span>
|
<span>模块:{{ $data['module']['name'] }}</span>
|
||||||
@elseif (isset($data['applications']) && !is_null($data['application']))
|
@elseif (isset($data['applications']) && !is_null($data['application']))
|
||||||
<span>应用程序:{{ $data['application']['name'] }}</span>
|
<span>应用程序:{{ $data['application']['name'] }}</span>
|
||||||
@ -23,18 +22,26 @@
|
|||||||
<p>{{ $data['meta']['description'] }}</p>
|
<p>{{ $data['meta']['description'] }}</p>
|
||||||
|
|
||||||
<br/>
|
<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')
|
@auth('web')
|
||||||
<form method="POST" action="{{ route('auth_request.store') }}">
|
<form method="POST" action="{{ route('auth_request.store') }}" class="mt-3">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="token" value="{{ $data['meta']['token'] }}">
|
<input type="hidden" name="token" value="{{ $data['meta']['token'] }}">
|
||||||
<button type="submit" class="btn btn-primary">同意</button>
|
<button type="submit" class="btn btn-primary">同意</button>
|
||||||
|
@ -45,12 +45,19 @@
|
|||||||
|
|
||||||
<form action="{{ route('token.new') }}" name="newToken" method="POST">
|
<form action="{{ route('token.new') }}" name="newToken" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="form-floating mb-2">
|
<div class="form-floating mb-2">
|
||||||
<input type="text" class="form-control" placeholder="Token 名称"
|
<input type="text" class="form-control" placeholder="Token 名称"
|
||||||
aria-label="密钥名称" name="name" required maxlength="25">
|
aria-label="密钥名称" name="name" required maxlength="25">
|
||||||
<label>Token 名称</label>
|
<label>Token 名称</label>
|
||||||
</div>
|
</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 type="submit" class="btn btn-primary visually-hidden">
|
||||||
创建
|
创建
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user