laragear/email-login

通过电子邮件1分钟内验证用户。

v1.0.0 2024-09-09 00:25 UTC

This package is auto-updated.

Last update: 2024-09-10 18:59:16 UTC


README

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

通过电子邮件1分钟内验证用户。

<form method="post" action="/auth/email/send">
    @csrf
    <input type="email" name="email" placeholder="me@email.com">
    <button type="submit">Log in</button>
</form>

保持此包免费

您的支持使我能够保持此包免费、更新和可维护。或者,您可以通过 传播信息!

安装

然后调用Composer来检索包。

composer require laragear/email-login

1分钟快速开始

在安装后,Email Login非常简单:将您想验证的用户电子邮件放入表单中,然后就会发送一封包含单次链接的电子邮件给他以进行验证。

首先,使用email-login:install Artisan命令安装配置文件和基础控制器。

php artisan email-login:install

之后,确保您已使用包含在Laragear\EmailLogin\Http\Routes类的路由注册器帮助程序注册了登录电子邮件将用于验证的路由。

use Illuminate\Routing\Route;
use Laragear\EmailLogin\Http\Routes as EmailLoginRoutes;

Route::view('welcome');

// Register the default Mail Login routes
EmailLoginRoutes::register();

提示

除了控制器外,您还可以通过参数更改电子邮件登录的路由路径。

use Laragear\EmailLogin\Http\Routes as EmailLoginRoutes;

EmailLoginRoutes::register(
    send: '/send-email-here',
    login: '/login-from-email-here',
    controller: 'App/Http/Controllers/MyEmailLoginController',
);

最后,通过在应用程序的任何位置进行POSTauth/email,添加一个“登录”框来接收用户电子邮件。

<form method="post" action="/auth/email/send">
    @csrf
    <input type="email" name="email" placeholder="me@email.com">
    <button type="submit">Log in</button>
</form>

就这样,您的用户就可以用电子邮件登录了。

此包会为您处理整个逻辑,但您始终可以使用自己的路由和控制器完全手动操作。

发送登录电子邮件

要手动实现登录电子邮件,您需要从表单提交中捕获用户凭据。EmailLoginRequest为您做了大部分工作。

如果您使用的是Laravel默认设置,请求将自动验证电子邮件。您只需要返回sendAndBack()方法以将用户重定向回表单。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->sendAndBack();
});

如果您需要在发送电子邮件之前执行某些操作,可以单独使用send()back(),如果要扩展电子邮件验证规则,则可以使用validate()方法。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    $email->validate([
        'email' => 'required|email:rfc,dns'
    ]);
    
    $email->send();
    
    session()->flash('message', 'Email sent successfully!');

    return back();
});

sendAndBack()send()都返回true,如果找到具有凭据的用户并发送了电子邮件(或已排队待发送),则返回false,如果用户不存在。某些应用程序可以通过隐藏用户存在性而没问题,但某些应用程序可能希望显示用户不存在。

if ($email->send()) {
    session()->flash('message', 'Email sent successfully!');
} else {
    throw ValidationException::withMessages([
        'email' => 'The user with the email does not exist'
    ]);
}

自定义凭据

在发送电子邮件登录时,验证的数据用于通过守卫的用户提供者查找要验证的用户。例如,如果您验证了username键,则只使用该键来查找用户并发送电子邮件。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    $email->validate([
        'username' => 'required|string|exists:users'
    ]);
    
    return $email->sendAndBack();
});

您可以使用带有请求输入中用作凭据的键的列表的withCredentials()方法来覆盖凭据以查找用户。该列表可能不同于验证的请求输入。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    $email->validate([
        // ...
    ]);
    
    return $email->withCredentials(['username', 'mail'])->sendAndBack();
});

键也可以是接收查询以查找用户的回调。

$email->withCredentials([
    'mail',
    fn($query) => $query->where('is_human', '>', 0.5)
]);

或者,如果您提供键值对,则键的值将用作凭据值。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    $email->validate([
        'email' => 'required|email'
    ]);
    
    return $email->withCredentials([
        'mail' => $email->email,
        'banned_at' => null,
        fn($query) => $query->where('standing', '>', 0.5)
    ])->sendAndBack();
});

登录过期

通过电子邮件发送的登录链接有一个过期时间,默认为5分钟。您可以通过配置或使用withExpiration()方法在运行时更改它,方法包括传递分钟数、DateTimeInterface实例或传递给strtotime()的字符串。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withExpiration(10)->sendAndBack();
});

自定义记住键

如果您的请求包含remember键,并且它是true的,则在用户登录时,用户将被记住。如果键不同,您可以通过withRemember()方法设置带有键名的字符串。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withRemember('remember_me')->sendAndBack();
});

或者,可以发出任何其他内容作为条件,例如布尔值或回调。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withRemember($email->boolean('remember_me'))->sendAndBack();
});

指定守卫

默认情况下,电子邮件登录假设用户将使用默认守卫进行身份验证,在大多数vanilla Laravel应用程序中是web。您可能想要在配置中更改默认守卫,或使用withGuard()在运行时更改它。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withGuard('admin')->sendAndBack();
});

电子邮件URL链接

您可以通过配置更改电子邮件登录将指向的URL,或使用withPath()withAction()withRoute()方法在运行时更改。如果需要,您还可以使用数组作为第二个参数设置参数。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withRoute('auth.email.login', ['is_cool' => true])->sendAndBack();
});

您还可以使用withParameters()方法仅向配置中设置的默认URL追加查询参数。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withParameters(['is_cool' => true])->sendAndBack();
});

警告

必须存在该路由。此路由应显示一个登录表单,而不是立即登录用户。请参阅从邮件登录

自定义Mailable

使用自己的Mailable类的基本方法是通过withMailable()方法设置它,可以是类名(由容器实例化)或Mailable实例。

use App\Mails\MyLoginMailable;
use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withMailable(MyLoginMailable::class)->sendAndBack();
});

或者,您可能想使用回调来自定义包含的Mailable实例。该回调接收LoginEmail mailable。在回调内部,您可以自由修改mailable,例如更改视图或目的地,甚至返回一个新的Mailable。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;
use Laragear\EmailLogin\Mails\LoginEmail;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withMailable(function (LoginEmail $mailable) {
        $mailable->view('my-login-email', ['theme' => 'blue']);
        
        $mailable->subject('Login to this awesome app');
    })->sendAndBack();
});

不透明节流

如果您想不透明地节流发送电子邮件,只需使用withThrottle()方法并指定秒数。在这段时间内,电子邮件将不会发送。这可以避免大量电子邮件。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    return $email->withThrottle(30)->sendAndReturnBack();
});

节流使用与存储电子邮件登录意图相同的缓存和请求指纹(IP),默认情况下。您可以将缓存存储更改为使用第二个名称,甚至可以将键作为第三个参数用于节流器。

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;

Route::post('/auth/email/send', function (EmailLoginRequest $email) {
    $key = strtolower($email->input('email'));

    return $email->withThrottle(30, 'redis', $key)->sendAndReturnBack();
});

添加元数据

您可以使用withMetadata()方法保存仅对登录尝试有效的数据。您可以在其中设置键和值的数组,您可以在登录成功时稍后检索

use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\EmailLoginRequest;
use Laragear\EmailLogin\Http\Requests\LoginByEmailRequest;

// Send the email and save the metadata internally.
Route::post('/auth/email/send', function (EmailLoginRequest $request) {
    return $request
        ->withMetadata(['is_cool' => true])
        ->sendAndReturnBack();
});

// Show the login form with the metadata.
Route::get('/auth/email/login', function (LoginByEmailRequest $request) {
    return view('laragear::email-login.web.login', [
        'is_cool' => $request->metadata('is_cool')
    ]);
});

提示

元数据不会在电子邮件链接中传输,而是作为电子邮件登录意图的一部分存储在您的应用程序缓存中。

从邮件登录

从电子邮件的登录过程必须在两个控制器操作中完成:一个显示表单,另一个验证用户。这两个路由都应该使用guest中间件,以避免被已验证的用户触发。

警告

登录必须在两个控制器动作中完成,因为 一些电子邮件客户端和服务器会预加载、缓存和/或预取登录链接。虽然这通常是为了加速导航或过滤恶意网站,但这会意外地让用户在非其设备上登录,并导致后续的登录尝试失败。

为了避免这种意外的身份验证,创建一个显示登录表单的路由,以及另一个用于验证用户的路由。

使用 LoginByEmailRequest 返回包含登录表单的视图,并在两个用户上登录用户。

  • 当登录无效或过期时,会向用户显示 HTTP 419(过期)错误,而不是视图。否则,您可以使用包含的 laragear::email-login.web.login 视图来显示表单。
  • 当收到登录表单提交时,用户将被自动登录。
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\Http\Requests\LoginByEmailRequest;

Route::middleware('guest')->group(function () {
    // Show the form to log in. 
    Route::get('/auth/login/mail', function (LoginByEmailRequest $request) {
        return view('laragear::email-login.web.login')
    })->name('login.mail');
    
    // User logged in automatically, show him the dashboard. 
    Route::post('/auth/login/mail', function (LoginByEmailRequest $request) {
        return $request->toIntended()
    });
})

检索元数据

如果您在发送电子邮件之前 设置了元数据,则可以使用 metadata() 方法以及 dot.notation 中的键来检索它,并可选地提供一个默认值,如果未设置。

use Laragear\EmailLogin\Http\Requests\LoginByEmailRequest;
use Illuminate\Support\Facades\Route;

Route::get('auth/login/mail', function (LoginByEmailRequest $request) {
    return view('laragear::email-login.web.login', [
        'is_cool' => $request->metadata('is_cool');
    ]);
});

电子邮件登录代理

如果您想要一种更 手动 的方式来登录用户,请使用 EmailLoginBroker,这是表单请求助手在幕后使用的。

要创建电子邮件登录意向,请使用 create()。它需要身份验证守卫、用户 ID 和过期时间。它返回一个随机令牌,该令牌应通过电子邮件传输。

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\EmailLoginBroker;use Laragear\EmailLogin\Mails\LoginEmail;

Route::post('/send-login-email', function (Request $request, EmailLoginBroker $broker) {
    $request->validate([
        'email' => 'required|email'
    ]);
    
    // Find the user by the email
    $user = User::where('email', $request->email)->first();
    
    // Send the email if the user exists.
    if ($user) {
        $url = url('/login-by-email', [
            'token' => $broker->create('web', $user->id),
            'guard' => 'web',
        ]);
        
        // Send the email with the url to the user.
        LoginEmail::make($user, $url)->to($request->email)->send();
    }
    
    session()->flash('message', 'Login email sent successfully!');
    
    return back();
});

当用户被重定向到您的电子邮件登录表单后,请使用带有令牌的 get() 方法检索 EmailLoginIntent

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;use Illuminate\Support\Facades\Route;
use Laragear\EmailLogin\EmailLoginBroker;use Laragear\EmailLogin\Mails\LoginEmail;

Route::get('/login-by-email', function (Request $request, EmailLoginBroker $broker) {
    // If the intent exists, show him the login form.
    if ($broker->get($request->query('token'))) {
        return view('my-email-login-view');
    }
    
    // If it doesn't exist, redirect the user back to the initial login.
    return redirect('send-login-email');
});

一旦收到表单提交,请使用 pull() 方法从缓存存储中删除意向,并使用 EmailLoginIntent 实例数据登录用户。

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Laragear\EmailLogin\EmailLoginBroker;

Route::post('/login-by-email', function (Request $request, EmailLoginBroker $broker) {
    $intent = $broker->pull($request->query('token'));
    
    // If the intent doesn't exist, bail out.
    if (!$intent) {
        return redirect('send-login-email');
    }

    // Log in the user using the intent data.
    Auth::guard($intent->guard)->loginUsingId($intent->id, $intent->remember);

    // Regenerate the session for security.
    $request->session()->regenerate();

    return redirect('/dashboard');
});

自定义令牌字符串

在生成电子邮件登录令牌时,将生成一个随机的 ULID。您可以通过设置一个回调来更改默认的生成器,该回调接收 EmailLoginIntent 并返回一个(希望非常)随机的字符串。您可以在 AppServiceProvider::register() 中这样做。

use Illuminate\Support\Str;
use Laragear\EmailLogin\EmailLoginBroker;
use Laragear\EmailLogin\EmailLoginIntent;

public function register()
{
    EmailLoginBroker::$tokenGenerator = function (EmailLoginIntent $intent) {
        return Str::random(128);
    };
}

高级配置

邮件登录旨在开箱即用,但您可以通过发布配置文件来覆盖配置,如果您不是使用 Laravel 的默认设置。

php artisan vendor:publish --provider="Laragear\EmailLogin\EmailLoginServiceProvider" --tag="config"

之后,您将收到一个类似于下面的 config/email-login.php 配置文件

return [
    'guard' => null,
    'route' => [
        'name' => 'login.mail',
        'view' => 'laragear::email-login.web.login',
    ],
    'throttle' => [
        'store' => null,
        'prefix' => 'throttle'
    ],
    'expiration' => 5,
    'cache' => [
        'store' => null,
        'prefix' => 'email-login'
    ],
    'mail' => [
        'mailer' => null,
        'connection' => null,
        'queue' => null,
        'view' => 'laragear::email-login.mail.login',
    ],
];

守卫

return [
    'guard' => null,
];

要使用的默认身份验证守卫。当为 null 时,将回退到应用程序默认值,通常是 web。为守卫设置的 User Provider 用于查找用户。

路由名称 & 视图

return [
    'route' => [
        'name' => 'login.mail',
        'view' => 'laragear::email-login.web.login',
    ],
];

此命名路由在电子邮件中链接,其中包含用于登录用户的视图表单。

节流

return [
    'throttle' => [
        'store' => null,
        'prefix' => 'throttle'
    ],
];

节流电子邮件 时,将使用此配置来设置要使用的缓存存储和前缀。

缓存

return [
    'cache' => [
        'store' => null,
        'prefix' => 'email-login'
    ],
];

电子邮件登录意向被保存到给定持续时间的缓存中。您在这里可以更改用于存储它们的缓存存储和前缀。当为 null 时,它将使用默认的应用程序存储。

链接过期

return [
    'expiration' => 5,
];

当发送链接时,将生成一个带有过期时间的已签名 URL。您可以控制链接在被缓存存储清除之前保持有效的时间(以分钟为单位)。

邮件驱动程序

return [
    'mail' => [
        'mailer' => null,
        'connection' => null,
        'queue' => null,
        'markdown' => 'laragear::email-login.mail.login',
    ],
];

这指定了用于发送登录电子邮件的邮件驱动程序,以及接收它的队列连接和名称。当为 null 时,将回退到应用程序默认值,通常是 smtp

这还设置了用于创建电子邮件的默认视图,它 使用 Markdown

Laravel Octane 兼容性

  • 没有单例使用过时的应用程序实例。
  • 没有单例使用过时的配置实例。
  • 没有单例使用过时的请求实例。
  • 可写入的两个静态属性是
    • LoginByMailRequest::$destroyOnRegeneration
    • EmailLoginBroker::$tokenGenerator

使用此包与 Laravel Octane 一起使用不应有任何问题。

安全

如果您发现任何与安全相关的问题,请通过电子邮件 darkghosthunter@gmail.com 联系,而不是使用问题跟踪器。

发送电子邮件后的身份验证阻止。

一旦将登录电子邮件发送给用户,由于它不会检查除有效电子邮件登录意图之外的内容,所以 LoginByEmailRequest 将无法阻止身份验证过程。

例如,如果用户在发送登录电子邮件后被禁止,用户仍然可以进行身份验证。

为了避免这种情况,扩展 LoginByEmailRequest 并修改 login() 方法以在手动检索的用户上添加进一步的检查。然后,在您选择的登录控制器中使用此新类。

use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Validation\ValidationException;
use Laragear\EmailLogin\Http\Requests\LoginByEmailRequest;

class MyLoginRequest extends LoginByEmailRequest
{
    /**
     * Proceed to log in the user after a successful form submission.
     */
    protected function login(StatefulGuard $guard, mixed $id, bool $remember): void
    {
        $user = User::whereNull('banned_at')->find($id);
        
        if (!$user) {
            throw ValidationException::withMessages([
                'email' => 'The user for this email has been banned.'
            ]);
        }
        
        $guard->login($user, $remember);
    }
}

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件

Laravel 是 Taylor Otwell 的商标。版权所有 © 2011-2024 Laravel LLC。