翻译、衍生自:https://laravel-china.org/docs/laravel/5.5/authentication
想要快速开始?在全新的 Laravel 项目中执行 Artisan 命令 php artisan make:auth
和 php artisan migrate
即可急速创建一个具有完整功能的认证系统脚手架代码。之后,在浏览器地址栏输入 http://your-app.dev/register
就能进入注册页面,完整的路由列表请使用 php artisan route:list
命令在控制台查看。
使用 Laravel 构建一个认证系统前所未有的简单,因为这个功能是开箱就准备好了的。认证相关功能的配置文件是 config/app.php
。
作为核心功能,Laravel 的认证由「guards」和「providers」组成。Guards 定义请求用户的认证方式。例如,使用了 session
驱动的 web
Guard 就是用会话和 Cookie 的方式认证的。
Provider 定义从持久化存储设备里获得用户信息的方式,Laravel 开箱提供 Eloquent 和数据库 查询语句构造器 两种驱动方式的 Provider。你也可以自定义其他的 Provider。
如果你现在有些懵逼,没有关系!这些配置对于许多项目,都不需要修改,保持默认配置就行了。
Laravel 默认使用的 Provider 使用 Eloquent 驱动,对应的 Eloquent 模型是 App\User
。如果你项目里不使用 Eloquent 驱动,你还可以修改为使用 database
驱动,它是基于查询语句构造器查询的。
App\User
模型对应的数据库表中,要确保密码字段至少是 60 个字符长度,当然,保持默认的 255 字符长度是个不错的选择。
同时,还要保证你的 users
表中包含一个可为空的、字符串类型(100 个字符长度)的 remember_token
字段,这个字段用来保存用户勾选「记住我」选项时的令牌值。
在 Laravel 中已经预置了认证控制器,位于 App\Http\Controllers\Auth
目录下。RegisterController
处理用户注册,LoginController
处理用户登录,ForgortPasswordController
发送密码重置链接,ResetPasswordController
处理重置密码逻辑。每个控制器都使用了 trait 引入必要的实现方法。对许多项目来说,你可以一点都不修改它们。
执行下面这一条 Artisan 命令,就可以快速创建与认证功能相关的路由和视图文件。
php artisan make:auth
在一个全新的 Laravel 项目中,这条命令会生成布局、登录和注册视图,还有认证系统使用的所有路由,一个 HomeController
(让用户登录之后跳转到面板页面)。
使用 make:auth
创建的视图文件包括 resources/views/auth
目录下的。同时,还有一个布局文件 resources/views/layouts/app.balade.php
上面这些视图文件使用了 Boostrap 这个 CSS 框架,你也可以自定义使用的框架。
现在路由、视图和控制器,数据库表都有了,开始用起来吧!注册新用户,然后登录进去。
当用户成功登录后,会跳转到 /home
地址,这个地址是在 LoginController
、RegisterController
和 ResetPasswordController
中的 redirectTo
属性中设定的,你可以修改它,以满足你的需求。
protected $redirectTo = '/';
如果在跳转时,还有一些通用的跳转逻辑要处理的话,那么就定义成一个 redirectTo
方法:
protected function redirectTo()
{
return '/path';
}
redirectTo()
的优先级高于 redirectTo
属性。
Laravel 默认使用 email
作为认证关键字段,如果要自定义,在 LoginController
中定义一个 username
方法即可。例如,下面我们将 name
(用户名)字段作为认证关键字段:
public function username()
{
return 'name';
}
你也可以在认证和注册用户时,使用自定义「Guard」。然后在 LoginController
、RegisterController
和 ResetPasswordController
中的 guard
方法里使用它(还方法返回一个 Guard 实例对象)。
use Illuminate\Support\Facades\Auth;
protected function guard()
{
return Auth::guard('guard-name');
}
如果你需要修改注册页面的表单字段,或者要自定义保存用户到数据库里的逻辑,就需要修改 RegisterController
了。
RegisterController
的 validator
方法验证从前台注册页面传递过来的表单字段,这里定义了对应字段的取值规则。你也可以按照需要修改这里定义的规则。
RegisterController
的 create
方法用来在数据库中创建一个新的 App\User
记录(也就是用户数据),使用 Eloquent ORM。你也可以根据需要修改。
使用 Auth
门面访问认证用户。
use Illuminate\Support\Facades\Auth;
// 获得当前认证用户
$user = Auth::user();
// 获得当前认证用户的 ID
$user = Auth::id();
你也可以使用 Illuminate\Http\Request
实例访问认证用户。记住,在控制器方法里,Request
会被自动注入。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
/**
* Update the user's profile.
*
* @param Request $request
* @return Response
*/
public function update(Request $request)
{
// $request->user() returns an instance of the authenticated user...
}
}
如果用户登录了当前系统,那么 Auth
门面的 check
方法会返回 true
,否则返回 false
。
use Illuminate\Support\Facades\Auth;
if (Auth::check) {
// 用户已登录
}
虽然这种方式能判断用户登录与否,但是一般不这么做,而是在路由定义或者控制器构造函数中使用中间件,判断用户登录与否。咱继续往下看。
我们可以在定义路由的时候,顺便定义使用的中间件。Laravel 有一个 auth
中间件,在 Illuminate\Auth\Middleware\Authenticate
这个地方,它是在 App\Http\Kernel
中注册的,就可以用来判断用户的登录/认证了吗。
Route::get('profile', function () {
// 只有认证用户才能进来
})->middleware('auth');
当然,你也可以在控制器构造函数里附加中间件。
public function __construct()
{
$this->middleware('auth');
}
为路由附加中间件的时候,可以为认证用户指定使用的 Guard,这里指定的 Guard 对应 auth.php
中 guards
数组选项中的 Key 之一。
public function __construct()
{
$this->middleware('auth:api');
}
如果你使用的是 Laravel 内置的 LoginController
类,它天然包含了 Illuminate\Foundation\Auth\ThrottlesLogins
trait。默认,如果用户经过几次失败的登录尝试后,只能再等 1 分钟后才能登录。该节流限制是以用户的用户名/邮箱号地址和 IP 地址作为唯一标识的。
如果你不用 Laravel 提供的认证控制器。那么你需要直接使用 Laravel 认证类写认证逻辑了。不过别担心,不难。
我们通过 Auth
门面访问 Laravel 的认证服务,使用 attempt
方法。
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* @return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $hashedPassword])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
attempt
方法接收的是验证字段键值对,提供的数据会用来在数据库表中查找。在上面的例子里,我们在数据库里查询 email
字段,如果找到用户,就会将数据库中存储的哈希密码与提供的哈希密码比较,如果一样的话,用户登录成功。
用户登录成功,attempt
方法就会返回 true
,否则返回 false
。
redirect()
函数返回一个 Redirector
实例对象,该对象的 intended
方法会在用户认证成功后,重定向到用户之前(未进入登录页面前)要去的那个 URL 地址。也可以为 intended
方法添加一个参数—— fallback URI —— 如果 intended
方法去到的那个 URL 无效,就使用这个 URL 地址。
你也可以在调用 attempt
方法的时候添加额外要验证的字段。例如,登录时我们顺便检查用户的「激活」状态,只查询激活用户。
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// The user is active, not suspended, and exists.
}
这里登录使用的关键字段是 email
,你可以在登录控制器中定义 username
方法,自行修改使用的关键字段。
你可以在使用 Auth
门面的 guard
方法时,指定使用的 Guard。就是说,你项目里的认证方式可以有好几种,因为每个 Guard 都有可能对应一中的认证方式,其背后对应的可能是一张数据库表或者是一个 Eloquent Model,它们完全可以单独组织成一个饱满的认证方式。
比如,我们在 auth.php
中定义了一个 admin
Guard,与 web
Guard 不同的是,admin
Guard 是从 admin
数据库表里获取认证用户数据的,而 web
Guard 是从 users
数据库表里获取认证用户数据的。
if (Auth::guard('admin')->attempt($credentials)) {
//
}
注销账号,使用 Auth
门面的 logout
方法。该方法会从从会话里清除用户认证信息。
Auth::logout();
如果你需要提供「记住我」的功能,就要为 attempt
方法传递第二个布尔值参数,它会永远记住用户,直到用户手动注销。当然,users
表中必须包含一个 remember_token
列,用来保存「记住我」产生的令牌。
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}
如果使用内置的 LoginController
处理登录,那么已经具备了「记住我」功能,这是在引入的 trait 中定义的。
Auth
门面还提供一个 viaRemember
方法,用来判断认证用户是否是勾选了「记住我」功能的用户,如果是的话,就会返回 true
,否则返回 false
。
if (Auth::viaRemember()) {
//
}
如果你已经有一个用户实例了,那么把它带入 Auth::login
方法的登录系统。这是因为 App\User
模型实现了 Illuminate\Contracts\Auth\Authenticatable
这个接口,所以才可以这样用:
Auth::login($user);
// 登录然后「记住」用户
Auth::login($user, true);
当然,你也可以选择使用的 Guard 实例。
Auth::guard('admin')->login($user);
如果已知用户主键 ID,也可以登录系统。使用 Auth::loginUsingId
方法,它接收的是用户主键。
Auth::loginUsingId(1);
// 登录然后「记住」用户呀
Auth::loginUsingId(1, true);
还可以使用 Auth::once
方法针对单次请求来认证用户,这种方式的认证不使用会话和 Cookie,这在构建无状态的 API 时非常有帮助:
if (Auth::once($credentials)) {
//
}
HTTP 基本认证 提供了一种快速认证用户的方式(不需要额外的「登录」页面)。为了使用它,在路由上附加一个 auth.basic
中间件即可,这个中间件在 Laravel 中已经注册好了,你直接用就行。
Route::get('profile', function () {
// 只有认证用户才能进来
})->middleware('auth.basic');
定义好后,当你在浏览器中访问这个路由的时候,会出现一个弹框,让你填入用户名和密码。默认,auth.basic
中间件使用 email
作为登录的关键字段,你可以在 LoginController
中添加 username
方法覆盖此约定。
如果你是使用 PHP FastCGI,HTTP 基础认证可能会不生效。这时,将下面的两行配置内容添加到 .htaccess
文件即可:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
你也可以在不借助会话和 Cookie 的情况下使用 HTTP 基本认证,用来作为 API 认证使用,这称为「无状态 HTTP 基本认证」。我们需要新定义一个中间件,在中间件里调用 Auth
门面的 onceBasic
方法,如果 onceBasic
没有返回响应,则允许请求的进一步处理。
<?php
namespace Illuminate\Auth\Middleware;
use Illuminate\Support\Facades\Auth;
class AuthenticateOnceWithBasicAuth
{
/**
* Handle an incoming request.
*
* @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');
你可以使用 Auth
门面的 extend
方法自定义认证 Guard 驱动(需要传递 provider
),这是在服务提供者中注册的。比如,你就可以在现有的 AuthServiceProvider
中注册。
<?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
{
/**
* Register any application authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::extend('jwt', function ($app, $name, array $config) {
// Return an instance of Illuminate\Contracts\Auth\Guard...
return new JwtGuard(Auth::createUserProvider($config['provider']));
});
}
}
可以看到,extend
方法的回调函数里返回的是 lluminate\Contracts\Auth\Guard
的一个实现。Guard
这个接口里定义的方法,在你的自定义 Guard 驱动中都必须实现。之后,你就可以在 auth.php
配置文件中的 guards
选项中使用它了。
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
如果你不是使用关系型数据库保存用户数据,就需要实现自定义用户 Provider 驱动。这里会用到 Auth
门面的 provider
方法:
<?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) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new RiakUserProvider($app->make('riak.connection'));
});
}
}
然后就可以在 auth.php
配置文件中使用这个 Provider 驱动了。不过,首先要定义一个使用这个驱动的 Provider。
'providers' => [
'users' => [
'driver' => 'riak',
],
],
然后在 guards
配置中使用这个 Provider。
UserProvider
接口Illuminate\Contracts\Auth\UserProvider
的实现仅负责从持久化存储设备中获取 Illuminate\Contracts\Auth\Authenticatable
实现,这些持久化设备是指 MySQL,Riak 之类的系统。这两个接口允许 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);
}
retrieveById
方法接收的是用户主键,比如 MySQL 数据库里的自增 ID。最后返回匹配这个 ID 的 Authenticatable
的实现。
retrieveByToken
方法接收的是用户主键和「记住我」令牌(对应 remember_token
字段值)。和 retrieveById
方法一样,Authenticatable
的实现。
updateRememberToken
方法用新的 $token
值,更新 $user
的 remember_token
字段值。这个新的 $token
值可以来源于用户再次「记住我」的登录操作,或者是在注销账号时产生的。
retrieveByCredentials
接收的是一个数组,这个数组数据最终是传递给 Auth::attempt
方法的认证字段数据。这个方法会使用 where
条件查询子句,使用 $credentials['username']
的值在持久化设备里查询,并且返回一个 Authenticatable
实现。此方法不应尝试进行任何密码验证或验证。
validateCredentials
会比较 $user
和给定的 $credentials
数组来认证用户,此方法会使用 Hash::check
来比较 $user->getAuthPassword()
与 $credentials['password']
的值,并且返回一个布尔值 true
或者 false
,表示提供的认证数据是否正确。
Authenticatable
接口现在我们来看 UserProvider
中的方法,先来看 Authenticatable
接口的方法。记住,Provider 应该从 retrieveById
和 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
模型就实现了这个接口,所以你可以用它作为的实现参考例子。
在认证过程中,会触发许多事件,你可以在 EventServiceProvider
中为这些事件添加监听者。
/**
* The event listener mappings for the application.
*
* @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',
],
];
Original url: Access
Created at: 2018-10-10 17:28:53
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论