[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',
],
];
```
- 入门指南
- 安装
- 部署
- 基础功能
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- URL
- Session
- 表单验证
- 错误
- 日志
- 前端开发
- Blade 模板
- 本地化
- 脚手架
- 编译资源 Mix
- 安全相关
- 用户认证
- API 认证
- 综合话题
- 命令行
- 广播
- 缓存
- 集合
- 事件
- 文件存储
- 辅助函数
- 邮件发送
- 消息通知
- 扩展包开发
- 队列
- 任务调度
- 数据库
- 快速入门
- 查询构造器
- 分页
- 数据库迁移
- 数据填充
- Redis
- Eloquent ORM
- 快速入门
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View