ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] # 用户认证 ## 用户认证快速指南 ### 构建认证系统 ``` $ php artisan make:auth $ php artisan migrate ``` ### 生成路由信息 ``` // vendor/laravel/framework/src/Illuminate/Routing/Router.php auth方法 Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); // 禁用注册,删除 RegisterController 并修改路由声明 Auth::routes(['register' => false]); ``` ### 生成控制器文件 ``` app\Http\Controllers\HomeController.php ``` ### 生成视图文件 ``` resources/views/auth resources/views/layouts resources/views/home.blade.php ``` ### 自定义重定向路径 >[success] 默认重定向到 /home,修改以下控制器的 redirectTo 属性修改重定向 LoginController // 登录后跳转 RegisterController // 注册后跳转 ResetPasswordController // 重置密码后跳转 VerificationController // 验证后跳转 ``` protected $redirectTo = '/'; // 自定义生成重定向路径的逻辑,redirectTo 方法优先于 redirectTo 属性。 protected function redirectTo() { return '/path'; } ``` > 修改中间件 RedirectIfAuthenticated 的 handle 方法,用来重定向验证前的页面:/login、/register、/email/verify ### 自定义验证的用户字段 ``` // 使用 username 替换 email 字段来认证 public function username() { return 'username'; } ``` ### 自定义看守器 ``` // 自定义用户认证和注册的「看守器」 // 需要在 LoginController,RegisterController,ResetPasswordController 定义 guard 方法 use Illuminate\Support\Facades\Auth; protected function guard() { return Auth::guard('guard-name'); } ``` ### 自定义验证 / 存储 > 自定义 RegisterController 类,可以自定义注册信息,验证规则,存储数据方式等。 ### 检索认证用户 ``` use Illuminate\Support\Facades\Auth; // 获取当前通过认证的用户... $user = Auth::user(); // 获取当前通过认证的用户 ID... $id = Auth::id(); // 通过 Illuminate\Http\Request 实例来访问已认证的用户 <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ProfileController extends Controller { /** * 更新用户资料。 * * @param Request $request * @return Response */ public function update(Request $request) { // $request->user() 返回一个认证用户实例... } } ``` ### 确定当前用户是否已经认证 ``` use Illuminate\Support\Facades\Auth; if (Auth::check()) { // 用户已经登录了... } ``` ### 保护路由 ``` // 只允许通过认证的用户访问 Route::get('profile', function () { // 只有认证过的用户可以进入... })->middleware('auth'); // 在控制器使用中间件 public function __construct() { $this->middleware('auth'); } ``` ### 重定向未认证的用户 ``` // 修改 app/Http/Middleware/Authenticate.php 文件中的 redirectTo 函数 protected function redirectTo($request) { return route('login'); } ``` ### 指定看守器 ``` // 指定的看守器应该对应 auth.php 配置文件中 guards 数组中的的一个键 public function __construct() { $this->middleware('auth:api'); } ``` ### 登录限流 如果你使用 Laravel 内置的 `LoginController` 类,`Illuminate\Foundation\Auth\ThrottlesLogins` trait 已经包含在该控制器中了。默认情况下,如果用户多次尝试却无法提供正确的登录凭据,那么该用户在一分钟内将不能再次尝试登录。这种限流策略基于用户的用户名/邮箱地址及其 IP 地址的唯一性。 ## 手动验证用户 ``` <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /** * 处理身份验证尝试。 * * @param \Illuminate\Http\Request $request * * @return Response */ public function authenticate(Request $request) { $credentials = $request->only('email', 'password'); if (Auth::attempt($credentials)) { // 身份验证通过... return redirect()->intended('dashboard'); } } } ``` ### 指定额外条件 ``` if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // 用户存在,已激活且未被禁用。 } ``` 访问指定的看守器实例 ``` // 看守器名称需要在 auth.php 配置中设置过 if (Auth::guard('admin')->attempt($credentials)) { // } ``` 登出 ``` Auth::logout(); ``` 记住用户 ``` // 设置 attempt 方法的第二参数,永久保持用户登录状态 // 用户表需要包含字符串类型的 remember_token 列用于存储令牌 if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // 用户被记住... } ``` 其它身份验证方法 ``` // 验证用户实例,将已经存在的用户登入应用 Auth::login($user); // 登录并「记住」给定的用户... Auth::login($user, true); // 通过 ID 验证用户身份,通过 ID 将用户登录到应用 Auth::loginUsingId(1); // 登录并「记住」给定用户... Auth::loginUsingId(1, true); // 仅验证一次用户身份,在单次请求中将用户登录到应用 // 不使用 session 或 cookies,此方法有助于构建一个无状态 API if (Auth::once($credentials)) { // } ``` ## HTTP 基础认证 ``` // 使用 auth.basic 中间件快速验证用户,无需设置「登录」页面,默认 email 字段 Route::get('profile', function () { // 只有认证过的用户可以进入... })->middleware('auth.basic'); ``` ### FastCGI 的注意事项 ``` // HTTP 基础认证可能无法正常工作,请设置 .htaccess 文件 RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] ``` 无状态 HTTP 基础认证 ``` // 使用 HTTP 基础身份验证,而无需在会话中设置用户标识符 cookie,这对 API 的身份验证特别有用。 <?php namespace App\Http\Middleware; use Illuminate\Support\Facades\Auth; class AuthenticateOnceWithBasicAuth { /** * 处理传入的请求 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, $next) { return Auth::onceBasic() ?: $next($request); } } // 注册路由中间件 Route::get('api/user', function () { // 只有认证过的用户可以进入... })->middleware('auth.basic.once'); ``` ## 退出 ``` use Illuminate\Support\Facades\Auth; Auth::logout(); ``` ### 让其它设备上的 Session 失效 ``` // 确保 Illuminate\Session\Middleware\AuthenticateSession 中间件在 // app/Http/Kernel.php 类中的 web 中间件组中 'web' => [ // ... \Illuminate\Session\Middleware\AuthenticateSession::class, // ... ], // 将其它设备上的用户 Session 注销 use Illuminate\Support\Facades\Auth; Auth::logoutOtherDevices($password); ``` ## 添加自定义的看守器 ``` <?php namespace App\Providers; use App\Services\Auth\JwtGuard; use Illuminate\Support\Facades\Auth; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * 注册任意应用认证/授权服务。 * * @return void */ public function boot() { $this->registerPolicies(); Auth::extend('jwt', function ($app, $name, array $config) { // 返回一个 Illuminate\Contracts\Auth\Guard 实例... return new JwtGuard(Auth::createUserProvider($config['provider'])); }); } } // 在 auth.php 配置文件的 guards 配置中使用这个看守器 'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ], ``` ### 请求闭包看守器 ``` use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; /** * 注册任意应用认证/授权服务。 * * @return void */ public function boot() { $this->registerPolicies(); Auth::viaRequest('custom-token', function ($request) { return User::where('token', $request->token)->first(); }); } // 在 auth.php 配置文件的 guards 配置中使用这个看守器 'guards' => [ 'api' => [ 'driver' => 'custom-token', ], ], ``` ## 添加自定义用户提供器 ``` // 如果不使用传统的关系数据库存储用户,就需要使用自己的身份验证用户提供器扩展 Lavare <?php namespace App\Providers; use Illuminate\Support\Facades\Auth; use App\Extensions\RiakUserProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { /** * 注册任意应用身份验证 / 授权服务 * Register any application authentication / authorization services. * * @return void */ public function boot() { $this->registerPolicies(); Auth::provider('riak', function ($app, array $config) { // 返回 Illuminate\Contracts\Auth\UserProvider 实例... return new RiakUserProvider($app->make('riak.connection')); }); } } // 在 auth.php 配置文件中切换到新的用户提供器 'providers' => [ 'users' => [ 'driver' => 'riak', ], ], // 在 guards 配置中使用这个提供器 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ], ``` ### 用户提供器契约 `Illuminate\Contracts\Auth\UserProvider` 实现仅负责从 MySQL、Riak 等持久化存储系统中提取 `Illuminate\Contracts\Auth\Authenticatable` 实现。无论用户如何存储及用于表示它的类是什么类型,这两个接口都允许 Laravel 身份验证机制继续运行: 我们来看看 `Illuminate\Contracts\Auth\UserProvider` 契约: ``` <?php namespace Illuminate\Contracts\Auth; interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); } ``` `retrieveByToken` 函数通过用户的唯一 `$identifier` 和存储在 `remember_token`列的 「记住我」 令牌获取用户。与前一方法相同,它返回 `Authenticatable` 实现。 `updateRememberToken` 方法用新 `$token` 更新 `$user` 的 `remember_token` 列。在「记住我」登录校验成功或者用户登出时分配「刷新令牌」。 在尝试登录到应用时,`retrieveByCredentials` 方法接受凭证数组传递给 `Auth::attempt` 方法。此方法在底层持久化存储中「查询」与这些凭证匹配的用户。通常,此方法运行一个基于 `$credentials['username']` 的 「where」 条件,它应该返回一个 `Authenticatable` 实现。**此方法不就尝试进行任何密码校验或身份验证。** `validateCredentials` 方法应该比较给定的 `$user` 与 `$credentials` 来验证用户身份。例如,此方法或许应该使用 `Hash::check` 来比较 `$user->getAuthPassword()` 的值与 `$credentials['password']` 的值。它应该返回 `true` 或 `false` ,以表明用户密码是否有效。 身份验证契约 我们已经剖析了 `UserProvider` 的每个方法。下面再来看看 `Authenticatable` 契约。切记,用户提供器的 `retrieveById`、 `retrieveByToken` 和 `retrieveByCredentials` 方法将返回此接口的实例: ``` <?php namespace Illuminate\Contracts\Auth; interface Authenticatable { public function getAuthIdentifierName(); public function getAuthIdentifier(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); } ``` 这个接口很简单。 `getAuthIdentifierName` 方法应该返回用户 「主键」 列的名字, `getAuthIdentifier` 方法则返回用户 「主键」。在 MySQL 后台,它会是自增主键。 `getAuthPassword` 方法应该返回用户的哈希密码。此接口允许身份验证系统与任一 User 类一直工作,不管使用的是哪种 ORM 或抽象存储层。默认情况下,Laravel 的 `app` 目录会包含一个实现了此接口的 `User` 类,你可以以这个实现示例作为参考。 ## 事件 ``` // 在 app/Providers/EventServiceProvider.php 中添加事件监听器 // $ php artisan make:listener LogSuccessfulLogin /** * 应用的事件监听器映射。 * * @var array */ protected $listen = [ 'Illuminate\Auth\Events\Registered' => [ 'App\Listeners\LogRegisteredUser', ], 'Illuminate\Auth\Events\Attempting' => [ 'App\Listeners\LogAuthenticationAttempt', ], 'Illuminate\Auth\Events\Authenticated' => [ 'App\Listeners\LogAuthenticated', ], 'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ], 'Illuminate\Auth\Events\Failed' => [ 'App\Listeners\LogFailedLogin', ], 'Illuminate\Auth\Events\Logout' => [ 'App\Listeners\LogSuccessfulLogout', ], 'Illuminate\Auth\Events\Lockout' => [ 'App\Listeners\LogLockout', ], 'Illuminate\Auth\Events\PasswordReset' => [ 'App\Listeners\LogPasswordReset', ], ]; ```