Laravel ではユーザー認証は非常に簡単です。実際、ほとんどすべてがすでに設定されています。構成ファイルは config/auth.php にあり、認証サービスの動作を調整するためのドキュメントにわかりやすい構成オプションが含まれています。
基礎となるコードでは、Laravel の認証コンポーネントは「ガード」と「プロバイダー」で構成されます。たとえば、Laravel はセッション ストレージの状態を維持するためにユーザーがどのように認証を実装するかを定義します。 tokenguard、tokenguard は、認証されたユーザーがリクエストを送信するときに持参する「API トークン」です。
プロバイダーは、永続ストレージからユーザー情報を取得する方法を定義します。Laravel の基礎となるレイヤーは、必要に応じて、追加のプロバイダーを定義することもできます。
これらの用語を見て混乱したと感じても、あまり心配する必要はありません。ほとんどのアプリケーションでは、何も変更せずにデフォルトの認証構成を使用するだけでよいからです。
デフォルトでは、Laravel にはアプリディレクトリに Eloquent モデル AppUser が含まれています。このモデルはデフォルトの Eloquent 認証ドライバーで使用できます。アプリケーションが Eloquent を使用しない場合は、Laravel クエリ ビルダーを使用するデータベース認証ドライバーを使用できます。
AppUser モデルのデータベース テーブル構造を構築するときは、パスワード フィールドの長さが少なくとも 60 文字であることを確認してください。
また、users テーブルに、長さ 100 の null 許容の文字列型の remember_token フィールドが含まれていることを確認する必要があります。このフィールドは、アプリケーションによって維持される「remember me」のセッション トークンを格納するために使用されます。移行で $table->rememberToken(); を使用することで実現できます。
Laravel は、AppHttpControllersAuth 名前空間の下にある 2 つの認証コントローラーをすぐに提供し、AuthController が新しいユーザーの登録とログインを処理し、PasswordController が使用されます。ユーザーがパスワードを取得できるようにします。各コントローラーは特性を使用して、必要なメソッドを導入します。多くのアプリケーションでは、これら 2 つのコントローラーを変更する必要はまったくありません。
Laravel では、次のコマンドを実行することで、認証に必要なルートとビューをすばやく生成できます:
php artisan make:auth
このコマンドを実行すると、登録ビューとログイン ビューが生成されます。すべての認証ルートと同様に、HomeController も同時に生成されます。これは、ログインに成功した後、コントローラーの下のアクションにジャンプするためです。もちろん、このコマンドを使用せずに、アプリケーションのニーズに応じてこのコントローラーを完全にカスタマイズまたは削除することもできます。
上で述べたように、php 職人の make:auth コマンドは、認証に必要なすべてのビューを resource/views/auth ディレクトリに作成します。
make:auth コマンドは、アプリケーションの基本レイアウト ファイルを含む resource/views/layouts ディレクトリも作成します。これらのビューはすべて Bootstrap CSS フレームワークを使用しており、ニーズに応じてカスタマイズすることもできます。
独自の認証コントローラーのルーティングとビューを設定したので、新しいユーザー登録とログイン認証を実装しましょう。ブラウザーで定義されたルートにアクセスでき、認証コントローラーにはデフォルトで (特性経由で) 登録およびログイン ロジックがすでに含まれています。
カスタム パス
ユーザーがログイン認証を正常に完了すると、デフォルトで / にジャンプします。AuthController ジャンプ パスで redirectTo 属性を設定することで、ログインをカスタマイズできます。認証成功後:
protected $redirectTo = '/home';
ユーザーがログインと認証に失敗すると、デフォルトではログイン フォームに対応するページに戻ります。
カスタマイズされたガード
この機能を実装するには、AuthController に対応するガード属性を定義する必要があります。 auth.php の対応するガード構成:
protected $guard = 'admin';
カスタム検証/ストレージ
新規ユーザー登録に必要なフォームフィールドを変更するには、カスタマイズ 新しい ユーザー フィールドがデータベースにどのように保存されるかについては、AuthController クラスを変更できます。このクラスは、入力パラメータの検証とアプリケーションの新しいユーザーの作成を担当します。
AuthController の検証メソッドには、新規ユーザーの検証ルールが含まれています。必要に応じて、このメソッドをカスタマイズできます。
AuthController の create メソッドは、Eloquent ORM を使用してデータベースに新しい AppUser レコードを作成する役割を果たします。もちろん、独自のニーズに基づいてこの方法をカスタマイズすることもできます。
認証ファサードを通じて認証されたユーザーにアクセスできます。
$user = Auth::user();
さらに、ユーザーが認証された後、 IlluminateHttpRequest インスタンスを介して認証されたユーザー:
<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;use Illuminate\Routing\Controller;class ProfileController extends Controller{ /** * 更新用户属性. * * @param Request $request * @return Response */ public function updateProfile(Request $request) { if ($request->user()) { // $request->user() 返回认证用户实例... } }}
現在のユーザーが認証されているかどうかを確認します
ユーザーがアプリケーションにログインしているかどうかを確認するには、Auth の check メソッドを使用できます。ユーザーが認証されている場合は、true を返します:
if (Auth::check()) { // The user is logged in...}
また、特定のルート/コントローラーにアクセスする前に、ミドルウェアを使用してユーザーが認証されていることを確認することもできます。以下の保護:
路由中间件可用于只允许通过认证的用户访问给定路由。Laravel 通过定义在 app\Http\Middleware\Authenticate.php的 auth中间件来处理这一操作。你所要做的仅仅是将该中间件加到相应的路由定义中:
// 使用路由闭包...Route::get('profile', ['middleware' => 'auth', function() { // 只有认证用户可以进入...}]);// 使用控制器...Route::get('profile', [ 'middleware' => 'auth', 'uses' => 'ProfileController@show']);
当然,如果你正在使用控制器类,也可以在控制器的构造方法中调用 middleware方法而不是在路由器中直接定义:
public function __construct(){ $this->middleware('auth');}
指定一个Guard
添加 auth中间件到路由后,还需要指定使用哪个 guard 来实现认证:
Route::get('profile', [ 'middleware' => 'auth:api', 'uses' => 'ProfileController@show']);
指定的 guard 对应配置文件 auth.php中 guards数组的某个键。
如果你使用了 Laravel 内置的 AuthController类, 可以使用 Illuminate\Foundation\Auth\ThrottlesLoginstrait 来限制用户登录失败次数。默认情况下,用户在几次登录失败后将在一分钟内不能登录,这种限制基于用户的用户名/邮箱地址+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; // AuthController类的其它部分...}
当然,你也可以不使用 Laravel 自带的认证控制器。如果你选择移除这些控制器,你需要直接使用 Laravel 认证类来管理用户认证。别担心,这很简单!
我们将会通过 Auth门面来访问认证服务,因此我们需要确保在类的顶部导入了 Auth门面,让我们看看 attempt方法:
<?phpnamespace App\Http\Controllers;use Auth;use Illuminate\Routing\Controller;class AuthController extends Controller{ /** * 处理登录认证 * * @return Response */ public function authenticate() { if (Auth::attempt(['email' => $email, 'password' => $password])) { // 认证通过... return redirect()->intended('dashboard'); } }}
attempt方法接收键值数组对作为第一个参数,数组中的值被用于从数据表中查找用户,因此,在上面的例子中,用户将会通过 email的值获取,如果用户被找到,经哈希运算后存储在数据中的密码将会和传递过来的经哈希运算处理的密码值进行比较。如果两个经哈希运算的密码相匹配那么将会为这个用户开启一个认证Session。
如果认证成功的话 attempt方法将会返回 true。否则,返回 false。
重定向器上的 intended方法将用户重定向到登录之前用户想要访问的 URL,在目标 URL 无效的情况下备用 URI 将会传递给该方法。
指定额外条件
如果需要的话,除了用户邮件和密码之外还可以在认证查询时添加额外的条件,例如,我们可以验证被标记为有效的用户:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {// The user is active, not suspended, and exists.}
注:在这些例子中,并不仅仅限于使用 email 进行登录认证,这里只是作为演示示例,你可以将其修改为数据库中任何其他可用作“username”的字段。
访问指定 Guard 实例
你可以使用 Auth门面的 guard方法指定想要使用的 guard 实例,这种机制允许你在同一个应用中对不同的认证模型或用户表实现完全独立的用户认证。
传递给 guard方法的 guard 名称对应配置文件 auth.php中 guards配置的某个键:
if (Auth::guard('admin')->attempt($credentials)) {//}
退出
要退出应用,可以使用 Auth门面的 logout方法,这将会清除用户 Session 中的认证信息:
Auth::logout();
如果你想要在应用中提供“记住我”的功能,可以传递一个布尔值作为第二个参数到 attempt方法,这样用户登录认证状态就会一直保持直到他们手动退出。当然,你的 users表必须包含 remember_token字段,该字段用于存储“记住我”令牌。
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // The user is being remembered...}
如果你要“记住”用户,可以使用 viaRemember方法来判断用户是否使用“记住我”cookie进行认证:
if (Auth::viaRemember()) { //}
认证一个用户实例
如果你需要将一个已存在的用户实例登录到应用中,可以调用 Auth门面的 login方法并传入用户实例,传入实例必须是 Illuminate\Contracts\Auth\Authenticatable契约的实现,当然,Laravel 自带的 App\User模型已经实现了该接口:
Auth::login($user);
通过ID认证用户
要通过用户ID登录到应用,可以使用 loginUsingId方法,该方法接收你想要认证用户的主键作为参数:
Auth::loginUsingId(1);
一次性认证用户
你可以使用 once方法只在单个请求中将用户登录到应用,而不存储任何 Session 和 Cookie,这在构建无状态的 API 时很有用。 once方法和 attempt方法用法差不多:
if (Auth::once($credentials)) { //}
HTTP基本认证能够帮助用户快速实现登录认证而不用设置专门的登录页面,首先要在路由中加上 auth.basic中间件。该中间件是 Laravel 自带的,所以不需要自己定义:
Route::get('profile', ['middleware' => 'auth.basic', function() { // 只有认证用户可以进入...}]);
中间件加到路由中后,当在浏览器中访问该路由时,会自动提示需要认证信息,默认情况下, auth.basic中间件使用用户记录上的 email字段作为“用户名”。
FastCGI上注意点
如果你使用 PHP FastCGI,HTTP 基本认证将不能正常工作,需要在 .htaccess文件加入如下内容:
RewriteCond %{HTTP:Authorization} ^(.+)$RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
使用 HTTP 基本认证也不需要在 Session 中设置用户标识 Cookie,这在 API 认证中非常有用。要实现这个,需要定义一个调用 onceBasic方法的中间件。如果该方法没有返回任何响应,那么请求会继续走下去:
<?phpnamespace Illuminate\Auth\Middleware;use Auth;use Closure;class AuthenticateOnceWithBasicAuth{ /** * 处理输入请求. * * @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() { // 只有认证用户可以进入...}]);
大多数 web 应用提供了用户重置密码的功能,Laravel 提供了便利方法用于发送密码提示及执行密码重置而不需要你在每个应用中重新实现。
开始之前,先验证 App\User模型实现了 Illuminate\Contracts\Auth\CanResetPassword契约。当然,Laravel 自带的 App\User模型已经实现了该接口,并使用 Illuminate\Auth\Passwords\CanResetPasswordtrait来包含实现该接口需要的方法。
生成重置令牌表迁移
接下来,用来存储密码重置令牌的表必须被创建,Laravel 已经自带了这张表的迁移,就存放在 database/migrations目录。所有,你所要做的仅仅是运行迁移:
php artisan migrate
Laravel 自带了 Auth\PasswordController,其中包含了重置用户密码的相应逻辑。重置密码所需的路由都已经通过 make:auth命令自动生成了:
php artisan make:auth
和路由一样,重置密码所需的视图文件也通过 make:auth命令一并生成了,这些视图文件位于 resources/views/auth/passwords目录下,你可以按照所需对生成的文件进行相应修改。
定义好重置用户密码路由和视图后,只需要在浏览器中访问这些路由即可。框架自带的 PasswordController已经包含了发送密码重置链接邮件以及更新数据库中密码的逻辑。
密码被重置后,用户将会自动登录到应用并重定向到 /home。你可以通过定义上 PasswordController的 redirectTo属性来自定义密码重置成功后的跳转链接:
protected $redirectTo = '/dashboard';
注意:默认情况下,密码重置令牌一小时内有效,你可以通过修改 config/auth.php文件中的选项 reminder.expire来改变有效时间。
自定义认证 Guard
在配置文件 auth.php中,可以配置多个”guards“,以便用于实现多用户表独立认证,你可以通过添加 $guard属性到自带的 PasswordController控制器的方法来使用你选择的 guard:
/*** The authentication guard that should be used.** @var string*/protected $guard = 'admins';
自定义密码 broker
在配置文件 auth.php中,可以配置多个密码,以便用于重置多个用户表的密码 broker,同样,可以通过在自带的 PasswordController控制器中添加 $broker属性来使用你选择的 broker:
/*** The password broker that should be used.** @var string*/protected $broker = 'admins';
Laravel 中还可以使用Laravel Socialite 通过 OAuth 提供者进行简单、方便的认证,也就是社会化登录,目前支持使用Facebook、Twitter、LinkedIn、GitHub和Bitbucket进行登录认证。
要使用社会化登录,需要在 composer.json文件中添加依赖:
composer require laravel/socialite
安装完社会化登录库后,在配置文件 config/app.php中注册 Laravel\Socialite\SocialiteServiceProvider:
'providers' => [ // 其它服务提供者... Laravel\Socialite\SocialiteServiceProvider::class,],
还要在 app配置文件中添加 Socialite门面到 aliases 数组:
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
你还需要为应用使用的 OAuth 服务添加认证信息,这些认证信息位于配置文件 config/services.php,而且键为 facebook, twitter, linkedin, google, github或 bitbucket,这取决于应用需要的提供者。例如:
'github' => [ 'client_id' => 'your-github-app-id', 'client_secret' => 'your-github-app-secret', 'redirect' => 'http://your-callback-url',],
接下来,准备好认证用户!你需要两个路由:一个用于重定向用户到 OAuth 提供者,另一个用户获取认证后来自提供者的回调。我们使用 Socialite门面访问 Socialite :
<?phpnamespace App\Http\Controllers;use Socialite;use Illuminate\Routing\Controller;class AuthController extends Controller{ /** * 将用户重定向到GitHub认证页面. * * @return Response */ public function redirectToProvider() { return Socialite::driver('github')->redirect(); } /** * 从GitHub获取用户信息. * * @return Response */ public function handleProviderCallback() { $user = Socialite::driver('github')->user(); // $user->token; }}
redirect方法将用户发送到 OAuth 提供者, user方法读取请求信息并从提供者中获取用户信息,在重定向用户之前,你还可以在请求上使用 scope方法设置”作用域”,该方法将会重写已存在的所有作用域:
return Socialite::driver('github') ->scopes(['scope1', 'scope2'])->redirect();
当然,你需要定义路由到控制器方法:
Route::get('auth/github', 'Auth\AuthController@redirectToProvider'); Route::get('auth/github/callback', 'Auth\AuthController@handleProviderCallback');
获取用户信息
有了用户实例之后,可以获取用户的更多详情:
$user = Socialite::driver('github')->user();// OAuth Two Providers$token = $user->token;// OAuth One Providers$token = $user->token;$tokenSecret = $user->tokenSecret;// All Providers$user->getId();$user->getNickname();$user->getName();$user->getEmail();$user->getAvatar();
你可以通过 Auth门面的 extend方法定义自己的认证 guard,该功能需要在某个服务提供者的 boot方法中实现:
<?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的实现实例,该接口包含了自定义认证 guard 需要的一些方法。
定义好自己的认证 guard 之后,可以在配置文件的 guards配置中使用话这个 guard:
'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ],],
如果你没有使用传统的关系型数据库存储用户信息,则需要使用自己的认证用户提供者来扩展 Laravel。我们使用 Auth门面上的 provider方法定义自定义该提供者,在服务提供者调用 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) { // 返回Illuminate\Contracts\Auth\UserProvider实例... return new RiakUserProvider($app['riak.connection']); }); } /** * 在容器中注册绑定. * * @return void */ public function register() { // }}
通过 provider方法注册用户提供者后,你可以在配置文件 config/auth.php中切换到新的用户提供者。首先,在该配置文件定义一个使用新驱动的 providers数组:
'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的 Authenticatabl实现。
retrieveByToken函数通过唯一标识和存储在 remember_token字段中的“记住我”令牌获取用户。和上一个方法一样,该方法也返回 Authenticatabl实现。
updateRememberToken方法使用新的 $token更新 $user的 remember_token字段,新令牌可以是新生成的令牌(在登录是选择“记住我”被成功赋值)或者null(用户退出)。
retrieveByCredentials方法在尝试登录系统时获取传递给 Auth::attempt方法的认证信息数组。该方法接下来去底层持久化存储系统查询与认证信息匹配的用户,通常,该方法运行一个带“where”条件( $credentials[‘username’])的查询。然后该方法返回 UserInterface的实现。这个方法不做任何密码校验和认证。
validateCredentials方法比较给定 $user和 $credentials来认证用户。例如,这个方法比较 $user->getAuthPassword()字符串和经 Hash::make处理的 $credentials['password']。这个方法只验证用户认证信息并返回布尔值。
既然我们已经探索了 UserProvider上的每一个方法,接下来让我们看看 Authenticatable。该提供者应该从 retrieveById和 retrieveByCredentials方法中返回接口实现:
<?phpnamespace Illuminate\Contracts\Auth;interface Authenticatable { public function getAuthIdentifier(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName();}
这个接口很简单, getAuthIdentifier方法返回用户“主键”,在 MySQL 后台中是ID, 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', ],];