Laravel中實現(xiàn)用戶非常簡單。實際上,幾乎所有東西都已經(jīng)為你配置好了。配置文件位于config/auth.php
,其中包含了用于調(diào)整認(rèn)證服務(wù)行為的、文檔友好的選項配置。
默認(rèn)情況下,Laravel在app
目錄下包含了一個Eloquent模型App\User
,這個模型可以和默認(rèn)的Eloquent認(rèn)證驅(qū)動一起使用。如果你的應(yīng)用不使用Eloquent,你可以使用database
認(rèn)證驅(qū)動,該驅(qū)動使用了Laravel查詢構(gòu)建器。
為App\User
模型構(gòu)建數(shù)據(jù)庫表結(jié)構(gòu)的時候,確保password
字段長度至少有60位。
還有,你應(yīng)該驗證users
表包含了可以為空的、字符串類型的remember_token
字段長度為100,該字段用于存儲被應(yīng)用維護的”記住我(remember me)“的session令牌,這可以通過在遷移中使用$table->rememberToken();
來實現(xiàn)。
Laravel處理兩個認(rèn)證控制器,位于App\Http\Controllers\Auth和認(rèn)證,
PasswordController`包含幫助用戶找回密碼的邏輯。每個控制器都使用trait來引入它們需要的方法。對很多應(yīng)用而言,你根本不需要修改這兩個控制器。
默認(rèn)情況下,沒有路由將請求指向用戶認(rèn)證控制器,你要手動在app/Http/routes.php
文件中添加它們:
// 認(rèn)證路由...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');
// 注冊路由...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
盡管框架包含了用戶認(rèn)證控制器,你還是需要提供這些控制器可以渲染的視圖。這些視圖位于resources/views/auth
目錄,你可以按需自定義視圖是resources/views/auth/login.blade.php
,注冊視圖是resources/views/auth/register.blade.php
。
<!-- resources/views/auth/login.blade.php -->
<form method="POST" action="/auth/login">
{!! csrf_field() !!}
<div>
Email
<input type="email" name="email" value="{{ old('email') }}">
</div>
<div>
Password
<input type="password" name="password" id="password">
</div>
<div>
<input type="checkbox" name="remember"> Remember Me
</div>
<div>
<button type="submit">Login</button>
</div>
</form>
<!-- resources/views/auth/register.blade.php -->
<form method="POST" action="/auth/register">
{!! csrf_field() !!}
<div>
Name
<input type="text" name="name" value="{{ old('name') }}">
</div>
<div>
Email
<input type="email" name="email" value="{{ old('email') }}">
</div>
<div>
Password
<input type="password" name="password">
</div>
<div>
Confirm Password
<input type="password" name="password_confirmation">
</div>
<div>
<button type="submit">Register</button>
</div>
</form>
既然你已經(jīng)為自帶的認(rèn)證控制器設(shè)置好了路由和視圖,接下來就準(zhǔn)備為應(yīng)用注冊新用戶并進行登錄認(rèn)證。你可以在瀏覽器中訪問定義好的路由,認(rèn)證控制器已經(jīng)實現(xiàn)了認(rèn)證已存在用戶以及存儲新用戶到數(shù)據(jù)庫中的業(yè)務(wù)邏輯(通過trait)。
當(dāng)一個用戶成功進行登錄認(rèn)證后,將會跳轉(zhuǎn)到/home鏈接,你需要事先注冊一個路由來處理該跳轉(zhuǎn)。你可以通過在AuthController
中設(shè)置redirectPath
屬性來自定義post認(rèn)證之后的跳轉(zhuǎn)路徑:
protected $redirectPath = '/dashboard';
當(dāng)一個用戶登錄認(rèn)證失敗后,將會跳轉(zhuǎn)到/auth/login
鏈接。你可以通過定義AuthController
的loginPath
屬性來自定義post認(rèn)證失敗后的跳轉(zhuǎn)路徑:
protected $loginPath = '/login';
要修改新用戶注冊所必需的表單字段,或者自定義新用戶字段如何存儲到數(shù)據(jù)庫,你可以修改AuthController
類。該類負(fù)責(zé)為應(yīng)用驗證和創(chuàng)建新用戶。
AuthController
的validator
方法包含了新用戶的驗證規(guī)則,你可以隨意按需要自定義該方法。
AuthController
的create
方法負(fù)責(zé)使用Eloquent ORM在數(shù)據(jù)庫中創(chuàng)建新的App\User
記錄。你可以基于自己的需要隨意自定義該方法。
你可以通過Auth
門面訪問認(rèn)證用戶:
$user = Auth::user();
一旦用戶通過認(rèn)證后,你還可以通過Illuminate\Http\Request
實例訪問認(rèn)證用戶:
<?php
namespace 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() 返回認(rèn)證用戶實例...
}
}
}
要判斷某個用戶是否登錄到應(yīng)用,可以使用Auth
門面的check
方法,如果用戶通過認(rèn)證則返回true
:
if (Auth::check()) {
// The user is logged in...
}
此外,你還可以在用戶訪問特定路由/控制器之前使用中間件來驗證用戶是否通過認(rèn)證,想要了解更多,可以查看路由保護文檔。
路由中間件可用于只允許通過認(rèn)證的用戶訪問給定路由。Laravel通過定義在app\Http\Middleware\Authenticate.php
的auth
中間件來處理這一操作。你所要做的僅僅是將該中間件加到相應(yīng)的路由定義中:
// 使用路由閉包...
Route::get('profile', ['middleware' => 'auth', function() {
// 只有認(rèn)證用戶可以進入...
}]);
// 使用控制器...
Route::get('profile', [
'middleware' => 'auth',
'uses' => 'ProfileController@show'
]);
當(dāng)然,如果你正在使用控制器類,也可以在控制器的構(gòu)造方法中調(diào)用middleware
方法而不是在路由器中直接定義:
public function __construct(){
$this->middleware('auth');
}
如果你正在使用Laravel內(nèi)置的AuthController
類,Illuminate\Foundation\Auth\ThrottlesLogins
?trait 可以用于限制用戶登錄失敗次數(shù)。默認(rèn)情況下,用戶在幾次登錄失敗后將在一分鐘內(nèi)不能登錄,這種限制基于用戶的用戶名/郵箱地址+IP地址:
<?php
namespace 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類的其它部分...
}
當(dāng)然,你也可以不使用Laravel自帶的認(rèn)證控制器。如果你選擇移除這些控制器,你需要直接使用Laravel認(rèn)證類來管理用戶認(rèn)證。別擔(dān)心,這很簡單!
我們將會通過Auth
門面來訪問認(rèn)證服務(wù),因此我們需要確保在類的頂部導(dǎo)入了Auth
門面,讓我們看看attempt
方法:
<?php
namespace App\Http\Controllers;
use Auth;
use Illuminate\Routing\Controller;
class AuthController extends Controller{
/**
* 處理登錄認(rèn)證
*
* @return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// 認(rèn)證通過...
return redirect()->intended('dashboard');
}
}
}
attempt
方法接收鍵值數(shù)組對作為第一個參數(shù),數(shù)組中的值被用于從數(shù)據(jù)表中查找用戶,因此,在上面的例子中,用戶將會通過email
的值獲取,如果用戶被找到,經(jīng)哈希運算后存儲在數(shù)據(jù)中的密碼將會和傳遞過來的經(jīng)哈希運算處理的密碼值進行比較。如果兩個經(jīng)哈希運算的密碼相匹配那么一個認(rèn)證session將會為這個用戶開啟。
如果認(rèn)證成功的話attempt
方法將會返回true
。否則,返回false
。
重定向器上的intended
方法將會將用戶重定向到登錄之前用戶想要訪問的URL,在目標(biāo)URL無效的情況下備用URI將會傳遞給該方法。
如果你想的話,除了用戶郵件和密碼之外還可以在認(rèn)證查詢時添加額外的條件,例如,我們可以驗證被標(biāo)記為有效的用戶:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// The user is active, not suspended, and exists.
}
要退出應(yīng)用,可以使用Auth
門面的logout
方法,這將會清除用戶session中的認(rèn)證信息:
Auth::logout();
注意:在這些例子中,
如果你想要在應(yīng)用中提供“記住我”的功能,可以傳遞一個布爾值作為第二個參數(shù)到attempt
方法,這樣用戶登錄認(rèn)證狀態(tài)就會一直保持直到他們手動退出。當(dāng)然,你的users
表必須包含remember_token
字段,該字段用于存儲“記住我”令牌。
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// The user is being remembered...
}
如果你要“記住”用戶,可以使用viaRemember
方法來判斷用戶是否使用“記住我”cookie進行認(rèn)證:
if (Auth::viaRemember()) {
//
}
如果你需要將一個已存在的用戶實例登錄到應(yīng)用中,可以調(diào)用用戶實例上的login
方法,給定實例必須是Illuminate\Contracts\Auth\Authenticatable
契約的實現(xiàn),當(dāng)然,Laravel自帶的App\User
模型已經(jīng)實現(xiàn)了該接口:
Auth::login($user);
要通過用戶ID登錄到應(yīng)用,可以使用loginUsingId
方法,該方法接收你想要認(rèn)證用戶的主鍵作為參數(shù):
Auth::loginUsingId(1);
你可以使用once
方法只在單個請求中將用戶登錄到應(yīng)用,而不存儲任何session和cookie,這在構(gòu)建無狀態(tài)的API時很有用。once
方法和attempt
方法用法差不多:
if (Auth::once($credentials)) {
//
}
HTTP基本認(rèn)證能夠幫助用戶快速實現(xiàn)登錄認(rèn)證而不用設(shè)置專門的登錄頁面,首先要在路由中加上auth.basic
中間件。該中間件是Laravel自帶的,所以不需要自己定義:
Route::get('profile', ['middleware' => 'auth.basic', function() {
// 只有認(rèn)證用戶可以進入...
}]);
中間件加到路由中后,當(dāng)在瀏覽器中訪問該路由時,會自動提示需要認(rèn)證信息,默認(rèn)情況下,auth.basic
中間件使用用戶記錄上的email
字段作為“用戶名”。
FastCGI上注意點
如果你使用PHP FastCGI,HTTP基本認(rèn)證將不能正常工作,需要在.htaccess
文件加入如下內(nèi)容:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
使用HTTP基本認(rèn)證也不需要在session中設(shè)置用戶標(biāo)識cookie,這在API認(rèn)證中非常有用。要實現(xiàn)這個,需要定義一個調(diào)用onceBasic
方法的中間件。如果該方法沒有返回任何響應(yīng),那么請求會繼續(xù)走下去:
<?php
namespace 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() {
// 只有認(rèn)證用戶可以進入...
}]);
大多數(shù)web應(yīng)用提供了用戶重置密碼的功能,Laravel提供了便利方法用于發(fā)送密碼提示及執(zhí)行密碼重置而不需要你在每個應(yīng)用中重新實現(xiàn)。
開始之前,先驗證App\User
模型實現(xiàn)了Illuminate\Contracts\Auth\CanResetPassword
契約。當(dāng)然,Laravel自帶的App\User
模型已經(jīng)實現(xiàn)了該接口,并使用Illuminate\Auth\Passwords\CanResetPassword
?trait來包含實現(xiàn)該接口需要的方法。
接下來,用來存儲密碼重置令牌的表必須被創(chuàng)建,Laravel已經(jīng)自帶了這張表的遷移,就存放在database/migrations
目錄。所有,你所要做的僅僅是運行遷移:
php artisan migrate
Laravel自帶了Auth\PasswordController
,其中包含重置用戶必須的邏輯。然而,你需要定義一個路由將請求轉(zhuǎn)發(fā)到該控制器:
// 密碼重置鏈接請求路由...
Route::get('password/email', 'Auth\PasswordController@getEmail');
Route::post('password/email', 'Auth\PasswordController@postEmail');
// 密碼重置路由...
Route::get('password/reset/{token}', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');
除了為Auth\PasswordController
定義路由之外,還需要提供相應(yīng)視圖,別擔(dān)心,我們將會提供示例視圖來幫助你開始,當(dāng)然,你也可以自定義表單樣式。
你需要為密碼重置請求表單 提供HTML視圖,該視圖文件應(yīng)該放在resources/views/auth/password.blade.php
,表單提供了一個輸入用戶郵箱地址的字段,從而允許用戶從郵件中訪問密碼重置鏈接:
<!-- resources/views/auth/password.blade.php -->
<form method="POST" action="/password/email">
{!! csrf_field() !!}
<div>
Email
<input type="email" name="email" value="{{ old('email') }}">
</div>
<div>
<button type="submit">
Send Password Reset Link
</button>
</div>
</form>
當(dāng)一個用戶提交了重置密碼請求后,將會收到一封電子郵件,其中包含了一個鏈接,該鏈接指向PasswordController
的getReset
方法,你需要為該電子郵件創(chuàng)建一個視圖resources/views/emails/password.blade.php
。該視圖將會獲取包含密碼重置令牌的$token
變量,用于和用戶重置密碼請求進行匹配。下面是一個電子郵件視圖的例子:
<!-- resources/views/emails/password.blade.php -->
Click here to reset your password: {{ url('password/reset/'.$token) }}
當(dāng)用戶點擊電子郵件中的鏈接來重置密碼時,需要提交一個密碼重置表單,該視圖位于resources/views/auth/reset.blade.php
。
下面是一個密碼重置表單示例:
<!-- resources/views/auth/reset.blade.php -->
<form method="POST" action="/password/reset">
{!! csrf_field() !!}
<input type="hidden" name="token" value="{{ $token }}">
<div>
<input type="email" name="email" value="{{ old('email') }}">
</div>
<div>
<input type="password" name="password">
</div>
<div>
<input type="password" name="password_confirmation">
</div>
<div>
<button type="submit">
Reset Password
</button>
</div>
</form>
如果你已經(jīng)定義好路由和視圖來重置用戶密碼,只需要在瀏覽器中訪問這些路由即可??蚣茏詭У?code>PasswordController已經(jīng)包含了發(fā)送密碼重置鏈接郵件以及更新數(shù)據(jù)庫中密碼的邏輯。
密碼被重置后,用戶將會自動登錄到應(yīng)用并重定向到/home
。你可以通過定義上PasswordController
的redirectTo
屬性來自定義post密碼重置跳轉(zhuǎn)鏈接:
protected $redirectTo = '/dashboard';
注意:默認(rèn)情況下,密碼重置令牌一小時內(nèi)有效,你可以通過修改
config/auth.php
文件中的選項reminder.expire
來改變有效時間。
Laravel中還可以使用Laravel Socialite通過OAuth提供者進行簡單、方便的認(rèn)證,也就是社會化登錄,目前支持使用Facebook、Twitter、LinkedIn、GitHub和Bitbucket進行登錄認(rèn)證。
要使用社會化登錄,需要在composer.json
文件中添加依賴:
composer require laravel/socialite
安裝完社會化登錄庫后,在配置文件config/app.php
中注冊Laravel\Socialite\SocialiteServiceProvider
:
'providers' => [
// 其它服務(wù)提供者...
Laravel\Socialite\SocialiteServiceProvider::class,
],
還要在app
配置文件中添加Socialite
門面到aliases
數(shù)組:
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
你還需要為應(yīng)用使用的OAuth服務(wù)添加認(rèn)證信息,這些認(rèn)證信息位于配置文件config/services.php
,而且鍵為facebook
,?twitter
,linkedin
,?google
,?github
或bitbucket
,這取決于應(yīng)用需要的提供者。例如:
'github' => [
'client_id' => 'your-github-app-id',
'client_secret' => 'your-github-app-secret',
'redirect' => 'http://your-callback-url',
],
接下來,準(zhǔn)備好認(rèn)證用戶!你需要兩個路由:一個用于重定向用戶到OAuth提供者,另一個用戶獲取認(rèn)證后來自提供者的回調(diào)。我們使用Socialite
門面訪問Socialite?:
<?php
namespace App\Http\Controllers;
use Socialite;
use Illuminate\Routing\Controller;
class AuthController extends Controller{
/**
* 將用戶重定向到GitHub認(rèn)證頁面.
*
* @return Response
*/
public function redirectToProvider()
{
return Socialite::driver('github')->redirect();
}
/**
* 從GitHub獲取用戶信息.
*
* @return Response
*/
public function handleProviderCallback()
{
$user = Socialite::driver('github')->user();
// $user->token;
}
}
redirect
方法將用戶發(fā)送到OAuth提供者,user
方法讀取請求信息并從提供者中獲取用戶信息,在重定向用戶之前,你還可以在請求上使用scope
方法設(shè)置”作用域”,該方法將會重寫已存在的所有作用域:
return Socialite::driver('github')
->scopes(['scope1', 'scope2'])->redirect();
當(dāng)然,你需要定義路由到控制器方法:
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();
如果你沒有使用傳統(tǒng)的關(guān)系型數(shù)據(jù)庫存儲用戶信息,你需要使用自己的認(rèn)證驅(qū)動擴展Laravel。我們使用Auth門面上的extend
方法來定義自定義的驅(qū)動,你需要在服務(wù)提供者調(diào)用extend
方法:
<?php
namespace 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::extend('riak', function($app) {
// 返回Illuminate\Contracts\Auth\UserProvider實例...
return new RiakUserProvider($app['riak.connection']);
});
}
/**
* 在容器中注冊綁定.
*
* @return void
*/
public function register()
{
//
}
}
通過extend
方法注冊驅(qū)動后,你可以在配置文件config/auth.php
中切換到新的驅(qū)動。
Illuminate\Contracts\Auth\UserProvider
實現(xiàn)只負(fù)責(zé)從持久化存儲系統(tǒng)中獲取Illuminate\Contracts\Auth\Authenticatable
實現(xiàn),例如MySQL、Riak等等。這兩個接口允許Laravel認(rèn)證機制繼續(xù)起作用而不管用戶數(shù)據(jù)如何存儲或者何種類來展現(xiàn)。
讓我們先看看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數(shù)據(jù)中的自增ID。該方法獲取并返回匹配該ID的Authenticatabl
實現(xiàn)。
retrieveByToken
函數(shù)通過唯一標(biāo)識和存儲在remember_token
字段中的“記住我”令牌獲取用戶。和上一個方法一樣,該方法也返回Authenticatabl
實現(xiàn)。
updateRememberToken
方法使用新的$token
更新$user
的remember_token
字段,新令牌可以是新生成的令牌(在登錄是選擇“記住我”被成功賦值)或者null(用戶退出)。
retrieveByCredentials
方法在嘗試登錄系統(tǒng)時獲取傳遞給Auth::attempt
方法的認(rèn)證信息數(shù)組。該方法接下來去底層持久化存儲系統(tǒng)查詢與認(rèn)證信息匹配的用戶,通常,該方法運行一個帶“where”條件($credentials[‘username’])的查詢。然后該方法返回UserInterface
的實現(xiàn)。這個方法不做任何密碼校驗和認(rèn)證。
validateCredentials
方法比較給定$user
和$credentials
來認(rèn)證用戶。例如,這個方法比較$user->getAuthPassword()
字符串和經(jīng)Hash::make
處理的$credentials['password']
。這個方法只驗證用戶認(rèn)證信息并返回布爾值。
既然我們已經(jīng)探索了UserProvider
上的每一個方法,接下來讓我們看看Authenticatable
。該提供者應(yīng)該從retrieveById
和retrieveByCredentials
方法中返回接口實現(xiàn):
<?php
namespace 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
返回經(jīng)哈希處理的用戶密碼,這個接口允許認(rèn)證系統(tǒng)處理任何用戶類,不管是你使用的是ORM還是存儲抽象層。默認(rèn)情況下,Laravel自帶的app
目錄下的User
類實現(xiàn)了這個接口,所以你可以將這個類作為實現(xiàn)例子。
擴展閱讀1:實例教程 —— 使用Laravel內(nèi)置組件快速實現(xiàn)注冊登錄
擴展閱讀2:實例教程 —— 使用Laravel內(nèi)置組件快速實現(xiàn)密碼重置
擴展閱讀3:實例教程 —— 使用Socialite實現(xiàn)GitHub登錄認(rèn)證
更多建議: