i
`Merge branch 'master' into chufang-9-9
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller;
|
||||
|
||||
use app\adminapi\service\iam\IamLoginService;
|
||||
use think\facade\Log;
|
||||
|
||||
/** Dedicated optional endpoints: existing account and WeCom actions are untouched. */
|
||||
class IamController extends BaseAdminController
|
||||
{
|
||||
public array $notNeedLogin = ['config', 'start', 'callback', 'exchange'];
|
||||
private const COOKIE = 'ZYT_IAM_BROWSER';
|
||||
|
||||
public function config()
|
||||
{
|
||||
return $this->data(IamLoginService::settings())->header(['Cache-Control' => 'no-store']);
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
try {
|
||||
$service = new IamLoginService();
|
||||
$browser = $this->browser();
|
||||
if ($browser === '') {
|
||||
$browser = bin2hex(random_bytes(32));
|
||||
}
|
||||
setcookie(self::COOKIE, $browser, ['expires' => time() + 600, 'path' => '/adminapi/iam', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
|
||||
return redirect($service->start($browser, $this->request->ip()))->header($this->privateHeaders());
|
||||
} catch (\Throwable $error) {
|
||||
return $this->back(['iam_error' => $this->message($error)]);
|
||||
}
|
||||
}
|
||||
|
||||
public function callback()
|
||||
{
|
||||
try {
|
||||
if ($this->browser() === '' || $this->request->get('error', '') !== '') {
|
||||
throw new \RuntimeException('授权已取消或浏览器状态过期,请重新登录');
|
||||
}
|
||||
$ticket = (new IamLoginService())->callback($this->browser(), (string) $this->request->get('state', ''), (string) $this->request->get('code', ''));
|
||||
return $this->back(['iam_ticket' => $ticket]);
|
||||
} catch (\Throwable $error) {
|
||||
return $this->back(['iam_error' => $this->message($error)]);
|
||||
}
|
||||
}
|
||||
|
||||
public function exchange()
|
||||
{
|
||||
if (!$this->request->isPost()) {
|
||||
return $this->fail('请使用 POST 兑换登录状态')->code(405);
|
||||
}
|
||||
try {
|
||||
if ($this->browser() === '') {
|
||||
throw new \RuntimeException('浏览器登录状态已过期,请重新登录');
|
||||
}
|
||||
$payload = (new IamLoginService())->exchange($this->browser(), (string) $this->request->post('ticket', ''), (string) $this->request->header('origin', ''));
|
||||
return $this->data($payload)->header($this->privateHeaders());
|
||||
} catch (\Throwable $error) {
|
||||
return $this->fail($this->message($error))->header($this->privateHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
private function browser(): string
|
||||
{
|
||||
$value = (string) ($_COOKIE[self::COOKIE] ?? '');
|
||||
return preg_match('/^[a-f0-9]{64}$/D', $value) ? $value : '';
|
||||
}
|
||||
|
||||
private function back(array $query)
|
||||
{
|
||||
// Relative, fixed path: never derive the return origin from request headers or query strings.
|
||||
return redirect('/admin/login?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986))->header($this->privateHeaders());
|
||||
}
|
||||
|
||||
private function privateHeaders(): array
|
||||
{
|
||||
return ['Cache-Control' => 'no-store', 'Referrer-Policy' => 'no-referrer'];
|
||||
}
|
||||
|
||||
private function message(\Throwable $error): string
|
||||
{
|
||||
Log::warning('IAM login rejected: ' . get_class($error));
|
||||
$message = $error->getMessage();
|
||||
return preg_match('/^[\x{4e00}-\x{9fff}]/u', $message) ? $message : '统一账号登录失败,请重试或使用原账号登录';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user