|
@@ -0,0 +1,86 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+declare(strict_types=1);
|
|
|
|
|
+
|
|
|
|
|
+namespace Glacier\Core;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 会话管理封装(架构约定 docs/architecture.md §3.6)。
|
|
|
|
|
+ * - 安全 Cookie:HttpOnly + SameSite=Lax + Secure(生产自动开启)
|
|
|
|
|
+ * - session 名唯一化(glacier_sid)
|
|
|
|
|
+ * - CSRF Token 生成/校验(表单统一走中间件)
|
|
|
|
|
+ * - 登录后调用 regenerate() 防会话固定(P7 接入登录时使用)
|
|
|
|
|
+ */
|
|
|
|
|
+final class Session
|
|
|
|
|
+{
|
|
|
|
|
+ public static function start(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ if (session_status() === PHP_SESSION_ACTIVE) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ $debug = (bool) Config::get('app.debug', true);
|
|
|
|
|
+ session_name('glacier_sid');
|
|
|
|
|
+ session_set_cookie_params([
|
|
|
|
|
+ 'lifetime' => 0,
|
|
|
|
|
+ 'path' => '/',
|
|
|
|
|
+ 'domain' => '',
|
|
|
|
|
+ 'secure' => (bool) Config::get('app.secure_cookies', !$debug),
|
|
|
|
|
+ 'httponly' => true,
|
|
|
|
|
+ 'samesite' => 'Lax',
|
|
|
|
|
+ ]);
|
|
|
|
|
+ session_start();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function get(string $key, mixed $default = null): mixed
|
|
|
|
|
+ {
|
|
|
|
|
+ return $_SESSION[$key] ?? $default;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function set(string $key, mixed $value): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $_SESSION[$key] = $value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function has(string $key): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ return isset($_SESSION[$key]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static function remove(string $key): void
|
|
|
|
|
+ {
|
|
|
|
|
+ unset($_SESSION[$key]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 清空并销毁会话(登出)。 */
|
|
|
|
|
+ public static function destroy(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ $_SESSION = [];
|
|
|
|
|
+ if (ini_get('session.use_cookies')) {
|
|
|
|
|
+ $params = session_get_cookie_params();
|
|
|
|
|
+ setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
|
|
|
|
|
+ }
|
|
|
|
|
+ session_destroy();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 重置会话 ID(登录成功后调用,防会话固定)。 */
|
|
|
|
|
+ public static function regenerate(): void
|
|
|
|
|
+ {
|
|
|
|
|
+ session_regenerate_id(true);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 获取(或生成)CSRF Token。 */
|
|
|
|
|
+ public static function csrfToken(): string
|
|
|
|
|
+ {
|
|
|
|
|
+ if (empty($_SESSION['csrf_token'])) {
|
|
|
|
|
+ $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
|
|
|
+ }
|
|
|
|
|
+ return $_SESSION['csrf_token'];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 校验 CSRF Token(恒定时间比较)。 */
|
|
|
|
|
+ public static function csrfCheck(string $token): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ $stored = $_SESSION['csrf_token'] ?? '';
|
|
|
|
|
+ return $stored !== '' && hash_equals($stored, $token);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|