laravel では、認証サービスの中核はガードとプロバイダーで構成されます。ガードは、リクエストからユーザーを認証する方法を定義します。たとえば、laravel にはセッション ガードとトークン ガードが付属しています。セッション ガードは、保存されたセッションと Cookie からのユーザーを認証します。一方、トークン ガードは、API の API トークンを使用してユーザーを認証します。ユーザ認証。
プロバイダーは、永続ストレージからユーザーを取得する方法を定義します。 Laravel 自体は、Eloquent とデータベース クエリ ビルダーという 2 つの取得メソッドを提供します。もちろん、これを行うために追加のプロバイダーを追加することもできます。
これが少しわかりにくいと思われても、心配しないでください。ほとんどのアプリケーションでは、認証サービスのデフォルト構成情報を変更する必要はまったくありません。
データベースノート
AppUser モデルのデータベース テーブル構造を構築するときは、パスワードの長さが少なくとも 60 文字に保たれるようにする必要があり、デフォルトの 255 文字を選択することをお勧めします。
また、users テーブルに null 許容文字列列 remember_token が含まれていることを確認する必要があります。この列の長さは 100 文字である必要があります。このフィールドは、ユーザーが長期ログインを維持するためのトークン値を保存するために使用されます。移行で $table->rememberToken() を使用すると、この列をすばやく追加できます。
クイックスタート
ルーティング
php artisan make:auth
ビュー
make:auth コマンドは、resoucres/views/layouts ディレクトリも作成し、このディレクトリにアプリケーションの基本レイアウトを作成します。これらのビューはすべて、Bootstrap CSS フレームワークを使用しており、独自のニーズに応じてカスタマイズできます。
認証を実行する
カスタム リダイレクト アドレス
protected $rediredtTo = '/home';
カスタマイズされたガード
protected $guard = 'admin';
AuthController クラスの validate メソッドを使用して、検証フォームが検証ルールに一致することを確認します。このメソッドは必要に応じて変更できます。
AuthController クラスの create メソッドを使用して、データベースに新しいユーザー レコードを追加します。ここでは Eloquent ORM が使用されます。ニーズに合わせてこのメソッドを変更できます。
認証されたユーザーを取得する
$user = Auth::user();
<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class ProfileController extends Controller{ /** * Update the user's profile * * @param Request $request * @return Response */ public function updateProfile(Request $request) { if ($request->user()) { // $request->user() returns an instance of the authenticated user... } }}
你可以通过 Auth 假面的 check 方法来判断当前用户是否已经被认证。如果该用户已经被认证则会返回 true:
if (Auth::check()) { // The user is logged in...}
你应该使用中间件来过滤未被认证的用户访问某些路由或控制器。
路由中间件可以被用来限制只有已经被认证的用户才能访问所给定的路由。laravel 自带了 auth 中间件,该中间件被定义在 app\Http\Middleware\Authenticate.php 文件中。你只需要在定义路由时附加上该中间件就可以了:
// Using A Route Closure...Route::get('profile', ['middleware' => 'auth', function () { // Only authenticated users may enter... }]);// Using A Controller...Route::get('profile', [ 'middleware' => 'auth', 'uses' => 'ProfileController@show']);
当然,如果你使用控制器来注册路由,你可以在控制器的构造函数中使用 middleware 方法来附加中间件:
public function __construct(){ $this->middleware('auth');}
当你附加 auth 中间件到路由时,你也可以指定选择哪个守卫去提供认证:
Route::get('profile', [ 'middleware' => 'auth:api', 'uses' => 'ProfileController@show']);
所指定的守卫应该与配置文件 auth.php 中的 guards 数组键之一相匹配。
如果你使用了 laravel 內建的 AuthController 类,那么 Illuminate\Foundation\Auth\ThrottlesLogins trait 可以被用来限制用户尝试登陆的次数。默认的,当用户进行登陆数次失败时,其将会在一分钟内无法进行登陆。节流是根据用户的 username / e-mail 和 IP 地址来判定的:
<?phpnamespace App\Http\Controllers\Auth;use App\User;use Validator;use App\Http\Controllers\Controller;use Illuminate\Foundation\Auth\ThrottlesLogins;use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;class AuthController extends Controller{ use AuthenticatesAndRegistersUsers, ThrottlesLogins; // Rest of AuthController class...}
当然,你没有必要一定使用 laravel 內建的认证控制器。如果你选择删除这些认证控制器,那么你需要直接的使用 laravel 认证类来管理用户的认证。别担心,这当然不在话下。
我们将通过 Auth 假面来访问 laravel 的认证服务,所以,我们要确保在类文件的顶部引入 Auth 假面。接着,让我们查看一下 attempt 方法:
<?phpnamespace App\Http\Controllers;use Auth;class AuthController extends Controller{ /** * Handle an authentication attempt. * * @return Response */ public function authenticate() { if (Auth::attempt(['email' => $email, 'password' => $password])) { // Authentication passed... return redirect()->intended('dashboard'); } }}
attempt 方法接收一个键值对数组来作为第一个参数。数组中的值用来在数据库中查找相匹配的用户。所以,在上面的例子中,会返回匹配到 email 列为 $email 的用户。如果用户被找到,存储在数据库中的哈希后的密码会和数组中经过哈希加密的 password 值进行匹配。如果两个哈希后的值匹配成功的话,就会开启一个该用户已经认证的会话。
如果认证成功,则 attempt 方法会返回 true。否则返回 false。
intended 方法用来返回给重定向器用户在登录前所想要前往的 URL 地址,该方法也接收一个参数作为所请求地址不可用时的备用地址。
如果你需要,你也可以增加一些额外条件做认证查询,比如,你需要验证被标记为 'active' 的用户:
if (Auht::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // The user is active, not suspended, and exists.}
注意,在上面的例子中 email 并不是必备的选项,这仅仅只是作为一个示例。你可以使用任何进行用户认证的凭证来映射数据库中的字段。
你可以使用 Auth 假面的 guard 方法来获取你所需要的守卫实例。这使你可以在同一个应用中管理多种认证模型或用户表,并实现独立的认证。
guard 方法中所传递的守卫名称应该与配置文件 auth.php 中 guards 之一相匹配:
if (Auth::guard('admin')->attempt($credentials)) { //}
你可以使用 Auth 假面的 logout 方法来进行用户的退出,该方法将清除用户认证的会话信息:
Auth::logout();
如果你想要在你的应用中提供 记住我 的功能,你只需要在 attempt 方法中传递一个布尔值作为第二个参数,这将保持用户的认证信息直到用户手动的退出登录。当然,这要求你的用户表中必须包含 remember_token 列:
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // The user is being remembered...}
如果你作为被记住的用户登录的,那么你可以使用 viaRemember 方法来判断用户的认证是不是通过 记住我 的 cookie:
if (Auth::viaRemember()) { //}
如果你需要在应用中记录一个已经存在的用户实例,你可以使用 login 方法。所给定的参数必须是实现了 Illuminate\Contracts\Auth\Authenticatable 契约的一个实例。当然,在 laravel 中 App\User 模型已经实现了这个接口:
Auth::login($user);
当然,你也可以指定守卫实例:
Auth::guard('admin')->login($user);
你可以使用 loginUseingId 方法来通过 ID 记录用户的信息,该方法简单的接收一个需要进行认证的用户的主键:
Auth::loginUsingId(1);
once 方法只在当前请求中进行用户认证。不会存储会话或 cookies。这对构建无状态的 API 很有帮助。once 方法和 attempt 具有相同的签证方式:
if (Auth::once($credentials)) { //}
基于 HTTP 的认证提供了快速的用户认证机制而不用设立专门的登录页面。为了开始,你应该附加 auth.basic 中间件到你的路由。laravel 中內建了 auth.basic 中间件,所以你不需要去定义它:
Route::get('profile', ['middleware' => 'auth.basic', function () { // Only authenticated users may enter... }]);
一旦该中间件被附加到路由中,你每次访问该路由时都会被提示要求认证信息。默认的,auth.basic 中间件使用 email 列作为用户记录的用户名。
如果你使用 PHP FastCGI,基于 HTTP 的认证可能无法正常工作。你可以尝试在 .htaccess 文件中添加如下内容:
RewriteCond %{HTTP:Authorization} ^(.+)$RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
你也可以在使用 HTTP 基本认证时不使用 session 保存用户的身份到 cookie。这在基于 API 的认证尤其有效。为了做到这些,你可以定义一个中间件,然后调用 onceBasic 方法。如果 onceBasic 方法没有返回响应,那么请求将被进一步传递到应用:
<?phpnamespace Illuminate\Auth\Middleware;use Auth;use Closure;class AuthenticateOnceWithBasicAuth{ /** * Handle on incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return Auth::onceBasic() ?: $next($request); }}
接着,在路由中附加中间件:
Route::get('api/user', ['middleware' => 'auth.basic.once', function () { // Only authenticated users may enter... }]);
当然,你还需要在 kernel.php 核心中注册该中间件。
大多数的应用都提供了一种用户重置其忘记密码的方式。laravel 提供了一种便利的方式来发送密码提示和提供密码重置,这样你就不需要被迫在每个应用都实现一次这种机制。
为了方便开始,请确定你的 App\User 模型实现了 Illuminate\Contracts\Auth\CanResetPassword 契约,laravel 中自带的 App\User 模型中已经实现了这个借口,并且使用了 Illuminate\Auth\Passwords\CanResetPassword trait。
通过迁移生成密码重置表
接着,必须要创建一个用来存储重置密码的 token 的表。laravel 已经提供了这种表的迁移,并且存放在 database/migrations 目录下,所以,你只需要执行迁移命令:
php artisan migrate
laravel 自带的 Auth\PasswordController 控制器包含了所有重置密码所需的逻辑。所有提供密码重置的路由都可以通过 make:auth Artisan 命令来生成:
php artisan make:auth
当 make:auth 命令被执行时,laravel 会生成所有密码重置所需要的视图。这些视图将被存放在 resources/views/auth/passwords 目录中,你可以根据自己的需求去修改。
一旦你生成了所有重置密码所需要的路由和视图之后,你就可以通过在浏览器访问 /password/reset 路由来进行密码重置。PasswordController 已经包含了发送重置密码的链接邮件及更新密码到数据库的功能。
当密码被重置之后,用户会自动登录并且被重定向到 /home。你可以在 PasswordController 中定义 redirectTo 属性来定制化重定向地址:
protected $redirectTo = '/dashboard';
注意,默认的,密码重置的 token 有效期只有一个小时,你可以在 config/auth.php 配置文件中修改 expire 选项。
定制化认证守卫
在 auth.php 配置文件中,你可以配置多种 "guards",用于对各种用户表执行认证动作。你可以在 PasswordController 中使用 $guard 属性来选择守卫:
/** * The authentication guard that should be used. * * @var string */ protected $guard = 'admins';
定制化密码经纪人
在 auth.php 配置文件中,你可以配置多种密码“brokers”,用于对多种用户表进行密码重置。你可以通过在 PasswordController 中添加 $broker 属性来选择:
/** * The password broker that should be used. * * @var string */ protected $broker = 'admins';
你可以在服务容器中使用 Auth 假面的 extend 方法来定义自己的认证守卫:
<?phpnamespace App\Providers;use Auth;use App\Services\Auth\JwtGuard;use Illuminate\Support\ServiceProvider;class AuthServiceProvider extends ServiceProvider{ /** * Perform post-registration booting of services. * * @return void */ public function boot() { Auth::extend('jwt', function($app, $name, array $config) { // Return an instance of Illuminate\Contracts\Auth\Guard... return new JwtGuard(Auth::createUserProvider($config['provider'])); }); } /** * Register bindings in the container. * * @return void */ public function register() { // }}
你应该能从上面的示例中看到,你应该在 extend 方法中返回一个 Illuminate\Contracts\Auth\Guard 的实现。为了定制化守卫你需要实现这个接口所包含的方法。
一旦你的守卫被定义,你就可以在 auth.php 配置文件的 guards 选项中进行配置:
'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users' ],];
如果你并没有使用传统的关系数据库来存储你的用户,那么你需要提供你自己的用户提供者来扩展 laravel。你可以通过使用 Auth 假面的 provider 方法来定义自己的用户提供者。你应该在服务提供者中调用该方法:
<?phpnamespace App\Providers;use Auth;use App\Extensions\RiakUserProvider;use Illuminate\Support\ServiceProvider;class AuthServiceProvider extends ServiceProvider{ /** * Perform post-registration booting of services. * * @return void */ public function boot() { Auth::provider('riak', function($app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... return new RiakUserProvider($app['riak.connection']); }); } /** * Rigster bindings in the container. * * @return void */ public function register() { // }}
在你使用 provider 方法注册完成提供者之后,你需要在 config/auth.php 配置文件中切换你的用户提供者为新注册的提供者:
'providers' => [ 'users' => [ 'driver' => 'riak', ],],
然后,你需要在 guards 选项中使用该提供者:
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ],],
Illuminate\Contracts\Auth\UserProvider 的实现仅仅只是管理如何从持续存储系统中获取一个 Illuminate\Contracts\Auth\Authenticatable 的实现。如 MySQL,Riak,等等。这个两个接口实现了 laravel 的持续认证机制而不需要考虑用户数据是存放在哪里了或者存放的是什么类型。
让我们来看一下 Illuminate\Contracts\Auth\UserProvider 契约:
<?phpnamespace 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 方法接收用于识别用户身份的唯一标识 $identifier 和 remember_token 所存储“记住我”的 $token。就如上面的方法一样,该方法应该返回一个 Authenticatable 的实现。
updateRememberToken 方法使用新的 $token 来更新用户的 remember_token 字段。这个新的字段可以是用户登录成功并且设置了 记住我 而分配的,也可以是用户登出之后分配的 null。
retrieveByCredentials 方法接收一个数组凭证,它会在用户尝试登录时将凭证传递给 Auth::attemp 方法。该方法会询问底层持续存储设备凭证的匹配情况,一般该方法会使用 where 条件语句来进行查询 $credentials['username'] 类似的匹配状况。这个方法应该返回一个 UserInterface 的实现。不要在这个方法里做密码验证或者认证。
validateCredentials 方法应该比较所给定的用户和凭证的匹配情况。比如,这个方法或许会比较 $user-getAuthPassword() 和 Hash::make 后的 $credentials['password']。这个方法应该只做用户和凭证间的效验和返回比较情况的布尔值。
刚才我们探讨了 UserProvider 中的所有方法,现在让我们来看一看 Authenticatable 契约,你应该记得,提供者应该从 retrieveById 和 retrieveByCredentials 方法中返回该契约接口的实现:
<?phpnamespace 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 应该返回用户的经哈希后的密码。这个接口使认证系统可以在任何用户类中运行而不用管你使用的是什么 ORM 或者其它存储抽象层。默认的,laravel 在 app 目录下包含了 User 类,该类就实现了这个契约。所以你可以参照这个类的实现方式。
laravel 在认证进程中提供了各种各样的事件。你可以在你的 EventServiceProvider 中附加监听这些事件:
/** * The event listener mappings for the application. * * @var array */ protected $listen = [ 'Illuminate\Auth\Events\Attempting' => [ 'App\Listeners\LogAuthenticationAttempt', ], 'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ], 'Illuminate\Auth\Events\logout' => [ 'App\Listeners\logSuccessfulLogout', ], 'Illuminate\Auth\Events\Lockout' => [ 'App\Listeners\LogLockout', ], ];