Web Analytics

mirror

⭐ 74 stars Simplified Chinese by franbarbalopez

Mirror Logo

Latest Version on Packagist GitHub Tests Action Status Total Downloads License

Mirror

Mirror 是一个优雅的 Laravel 用户冒充包。它允许管理员无缝登录为其他用户,以排查问题、提供支持或测试用户体验。Mirror 通过加密验证、自动过期、多守卫支持、灵活的中间件和生命周期事件来处理会话完整性,适合需要可靠且安全用户冒充的生产环境应用。

功能

要求

安装

composer require franbarbalopez/mirror
可选 - 发布配置文件:

php artisan vendor:publish --tag=mirror

快速开始

1. 向用户模型添加 Trait

use Illuminate\Foundation\Auth\User as Authenticatable;
use Mirror\Concerns\Impersonatable;

class User extends Authenticatable { use Impersonatable;

public function canImpersonate(): bool { return $this->hasRole('admin'); }

public function canBeImpersonated(): bool { return ! $this->hasRole('super-admin'); } }

重要提示: 如果不实现 canImpersonate(),则每个人都可以模拟每个人。该 trait 默认返回 true

2. 开始模拟

use Mirror\Facades\Mirror;

public function impersonate(User $user) { Mirror::start($user);

return redirect()->route('dashboard'); }

3. 停止冒充

public function leave()
{
    Mirror::stop();

return redirect()->route('admin.users.index'); }

安全性

模拟会话通过使用您的应用密钥的 HMAC-SHA256 哈希进行保护。哈希涵盖了模拟者 ID、守卫名称、开始时间和重定向 URL。在每次调用 stop() 时,Mirror 会验证此哈希——如果有人篡改了会话,它会抛出异常并清除所有内容。

config/mirror.php 中配置 TTL,以便在设定时间后自动过期会话。

API 参考

开始模拟

通过用户实例:

Mirror::start($user);

// With redirect URLs $redirectUrl = Mirror::start( user: $targetUser, leaveRedirectUrl: route('admin.users.index'), startRedirectUrl: route('dashboard') );

return redirect($redirectUrl);

通过主键(适用于 int、UUID、ULID 等):

Mirror::startByKey(123);

Mirror::startByKey('550e8400-e29b-41d4-a716-446655440000');

通过电子邮件发送:

Mirror::startByEmail('user@example.com');

停止冒充

Mirror::stop();

// Force stop - bypasses TTL check but still verifies integrity Mirror::forceStop();

当需要结束管理员操作或清理脚本中的模拟身份时,使用 forceStop() —— 它会跳过 TTL 检查,但如果会话被篡改仍会抛出异常。

状态检查

Mirror::isImpersonating(): bool
Mirror::getImpersonator(): ?Authenticatable
Mirror::impersonatorId(): int|string|null
Mirror::getLeaveRedirectUrl(): ?string

别名

Mirror::as($user);           // same as start()
Mirror::leave();             // same as stop()
Mirror::impersonating();     // same as isImpersonating()
Mirror::impersonator();      // same as getImpersonator()

中间件

mirror.ttl

检查模拟会话是否已过期,并在需要时自动调用 stop()

Route::middleware('mirror.ttl')->group(function () {
    Route::get('/admin/users', [UserController::class, 'index']);
    Route::get('/admin/users/{user}', [UserController::class, 'show']);
});
适用于保护敏感的管理区域,希望过期会话能优雅退出。请注意,当TTL过期时,该中间件将结束模拟并重定向,因此请确保正确设置会话清理。

mirror.require

仅允许在主动模拟时访问:

Route::middleware('mirror.require')->group(function () {
    Route::get('/impersonation/banner', function () {
        return view('impersonation.banner');
    });
});

适用于仅在模拟身份时才有意义的特殊 UI 组件——例如显示您正在模拟谁的横幅。

mirror.prevent

在模拟身份时阻止访问:

Route::middleware('mirror.prevent')->group(function () {
    Route::post('/admin/users/{user}/delete', [UserController::class, 'destroy']);
    Route::get('/admin/settings', [SettingsController::class, 'edit']);
});

保护破坏性操作或敏感设置,这些操作或设置应仅由原始用户访问,而非冒充他人时访问。

授权

Impersonatable 特性提供两个方法,默认均返回 true。重写它们以添加您自己的逻辑:

use Mirror\Concerns\Impersonatable;

class User extends Authenticatable { use Impersonatable;

public function canImpersonate(): bool { return $this->hasRole('admin'); }

public function canBeImpersonated(): bool { return ! $this->hasRole('super-admin'); } }

你不需要这个特质——Mirror 会直接在你的用户模型中查找这些方法:

class User extends Authenticatable
{
    public function canImpersonate(): bool
    {
        return $this->hasPermission('impersonate-users');
    }

public function canBeImpersonated(): bool { return ! $this->is_system_account; } }

URL 重定向

您可以控制用户在开始和停止模拟时的去向:

public function impersonate(User $user)
{
    $redirectUrl = Mirror::start(
        user: $user,
        leaveRedirectUrl: route('admin.users.index'),  // where to go when they stop
        startRedirectUrl: route('dashboard')            // where to go right now
    );

return redirect($redirectUrl); }

public function leave() { Mirror::stop();

return redirect(Mirror::getLeaveRedirectUrl()); }

如果你没有指定 leaveRedirectUrl,它默认是调用 start() 时的当前 URL。

事件

Mirror 会触发两个事件,你可以监听:

这两个事件都包含冒充者、目标用户和守卫名称。适用于审计日志或触发工作流。

事件在响应发送给客户端之后触发,确保关键的冒充操作无延迟完成。对于像 mirror.ttl 这样可能在每个请求中运行的中间件尤其重要。

use Mirror\Events\ImpersonationStarted;

Event::listen(ImpersonationStarted::class, function (ImpersonationStarted $event) { // Log the activity to your audit system of choice Log::info('User impersonation started', [ 'impersonator_id' => $event->impersonator->id, 'impersonated_id' => $event->impersonated->id, 'guard' => $event->guardName, ]); });

性能与优化

Mirror 针对高性能应用进行了优化:

请求范围内缓存

冒充者模型在单个请求内被缓存,以避免重复的数据库查询:

// This first call will query the database
$impersonator = Mirror::getImpersonator();

// Subsequent calls in the same request use the cached instance, therefore this one will not: $impersonator = Mirror::getImpersonator();

这对于像 mirror.ttl 这样的中间件特别有益,它在每个请求中运行。

延迟事件分发

模拟事件在响应发送给客户端后分发,确保事件监听器不会影响响应时间。这样可以保持请求周期的快速,同时仍然允许审计日志记录和其他后台任务。

多守卫支持

Mirror 会自动检测您正在使用哪个守卫:

Auth::guard('admin')->login($admin);

Mirror::start($user); // uses 'admin' guard

Mirror::stop(); // restores to 'admin' guard

你不需要手动指定守卫——它会根据当前的认证上下文自动识别。

Blade 指令

@impersonating

@impersonating
    
You're impersonating {{ auth()->user()->name }}. Exit
@endimpersonating

{{-- Check specific guard --}} @impersonating('admin')

Impersonating via admin guard
@endimpersonating

@canImpersonate

@canImpersonate
    Manage Users
@endcanImpersonate

{{-- With guard --}} @canImpersonate('admin')

Admin tools
@endcanImpersonate

@canBeImpersonated

{{-- Check current user --}}
@canBeImpersonated
    Available for support
@endcanBeImpersonated

{{-- Check specific user --}} @canBeImpersonated($user)

@csrf
@endcanBeImpersonated

{{-- With guard --}} @canBeImpersonated($user, 'admin') @endcanBeImpersonated

许可证

MIT。参见 LICENSE.md

致谢

franbarbalopez 开发。

--- Tranlated By Open Ai Tx | Last indexed: 2025-12-07 ---