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 : '统一账号登录失败,请重试或使用原账号登录'; } }