跟蹤laravel自帶密碼重置的源代碼

最近使用laravel比較多,所以也鉆研了部分的源代碼。本文作為筆記已備日后查詢冀续。

首先從路由開始,laravel自帶認(rèn)證的密碼重置控制器路由在Illuminate\Routing\Router.php中。

 /**
     * Register the typical authentication routes for an application.
     *
     * @return void
     */
    public function auth()
    {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');

        // Registration Routes...
        $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
        $this->post('register', 'Auth\RegisterController@register');

        // Password Reset Routes...
        $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
        $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
        $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
        $this->post('password/reset', 'Auth\ResetPasswordController@reset');
    }

get('password/reset','Auth\ForgotPasswordController@showLinkRequestForm');
這個(gè)路由是用來(lái)返回密碼重置申請(qǐng)頁(yè)面的。這個(gè)很簡(jiǎn)單魂毁,不再多說(shuō)。

我們看到這個(gè)路由
post('password/email','Auth\ForgotPasswordController@sendResetLinkEmail');
這是用來(lái)發(fā)送郵件的出嘹,我們順著這一條追蹤下去席楚。

app\Http\Controllers\Auth\ForgotPasswordController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }
}

可以看到控制器里代碼很少,具體實(shí)現(xiàn)被封裝在了SendsPasswordResetEmailstrait里税稼。從代碼上面的引用可以輕松地通過(guò)命名空間找到
Illuminate\Foundation\Auth\SendsPasswordResetEmails.php

 /**
     * Send a reset link to the given user.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function sendResetLinkEmail(Request $request)
    {
        $this->validate($request, ['email' => 'required|email']);

        // We will send the password reset link to this user. Once we have attempted
        // to send the link, we will examine the response then see the message we
        // need to show to the user. Finally, we'll send out a proper response.
        $response = $this->broker()->sendResetLink(
            $request->only('email')
        );

        return $response == Password::RESET_LINK_SENT
                    ? $this->sendResetLinkResponse($response)
                    : $this->sendResetLinkFailedResponse($request, $response);
    }

看這個(gè)方法烦秩,首先驗(yàn)證請(qǐng)求的email是否合法,然后

$response = $this->broker()->sendResetLink(
            $request->only('email')
        );

首先調(diào)用了自身的broker()方法郎仆,我們來(lái)看一下

  /**
     * Get the broker to be used during password reset.
     *
     * @return \Illuminate\Contracts\Auth\PasswordBroker
     */
    public function broker()
    {
        return Password::broker();
    }

看注釋只祠,發(fā)現(xiàn)返回的實(shí)例類型是 Contracts下面的,Contracts下面定義的全部是接口扰肌,我們看不到內(nèi)部的實(shí)現(xiàn)抛寝,這下線索就斷掉了。沒(méi)關(guān)系曙旭,我們靈活一點(diǎn)盗舰,這里只要知道返回的是一個(gè)PasswordBroker接口的對(duì)象就行了。然后我們走捷徑桂躏,直接用編輯器的搜索功能搜sendResetLink這個(gè)方法就行了钻趋。因?yàn)槲覀兡繕?biāo)是看這封郵件是怎么發(fā)出去的。很容易就找到了這個(gè)方法的實(shí)現(xiàn)在
Illuminate\Auth\Passwords\PasswordBroker.php


    /**
     * Send a password reset link to a user.
     *
     * @param  array  $credentials
     * @return string
     */
    public function sendResetLink(array $credentials)
    {
        // First we will check to see if we found a user at the given credentials and
        // if we did not we will redirect back to this current URI with a piece of
        // "flash" data in the session to indicate to the developers the errors.
        $user = $this->getUser($credentials);

        if (is_null($user)) {
            return static::INVALID_USER;
        }

        // Once we have the reset token, we are ready to send the message out to this
        // user with a link to reset their password. We will then redirect back to
        // the current URI having nothing set in the session to indicate errors.
        $user->sendPasswordResetNotification(
            $this->tokens->create($user)
        );

        return static::RESET_LINK_SENT;
    }

首先從傳入的參數(shù)中獲得user剂习,驗(yàn)證user是否存在蛮位,然后重點(diǎn)來(lái)了

 $user->sendPasswordResetNotification(
            $this->tokens->create($user)
        );

我們先不急著找sendPasswordResetNotification,我們先看一下$this->tokens->create($user)较沪,在類的構(gòu)造方法中

 public function __construct(TokenRepositoryInterface $tokens,
                                UserProvider $users)
    {
        $this->users = $users;
        $this->tokens = $tokens;
    }

我們看到傳入了一個(gè)TokenRepositoryInterface接口的實(shí)例,從字面上我們能判斷出這個(gè)接口是和token生成有關(guān)的土至。我們直接搜索這個(gè)接口的實(shí)現(xiàn)购对。
Illuminate\Auth\Passwords\DatabaseTokenRepository.php這個(gè)類實(shí)現(xiàn)了TokenRepositoryInterface接口。我們看看create方法是怎樣定義的陶因。

  public function create(CanResetPasswordContract $user)
    {
        $email = $user->getEmailForPasswordReset();

        $this->deleteExisting($user);

        // We will create a new, random token for the user so that we can e-mail them
        // a safe link to the password reset form. Then we will insert a record in
        // the database so that we can verify the token within the actual reset.
        $token = $this->createNewToken();

        $this->getTable()->insert($this->getPayload($email, $token));

        return $token;
    }

果然這里面是生成了用于重置密碼的token骡苞,可以我們發(fā)現(xiàn)兩個(gè)奇怪的地方
1.參數(shù)傳入的是一個(gè)CanResetPasswordContract接口實(shí)例,這個(gè)實(shí)例竟然就是user,那么我們要看看這個(gè)接口和User模型之間有什么關(guān)系楷扬。從該文件的引用我們知道CanResetPasswordContract是一個(gè)別名解幽,真名叫Illuminate\Contracts\Auth\CanResetPassword但是我們不去找這個(gè)接口,因?yàn)槊髦朗墙涌诤嫫唬疫^(guò)去一定撲個(gè)空躲株。我們要找實(shí)現(xiàn)這個(gè)接口的位置,于是再次搜索镣衡。這下我們發(fā)現(xiàn)了Illuminate\Foundation\Auth\User.php

<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;
}

原來(lái)user模型實(shí)現(xiàn)了這個(gè)接口霜定。第一個(gè)疑點(diǎn)解決。下面我們來(lái)看create方法中的第二個(gè)疑點(diǎn)廊鸥。

2.這句話
$this->getTable()->insert($this->getPayload($email, $token));我們很容易可以知道是用來(lái)向password_reset表中寫入數(shù)據(jù)的望浩。但是我們沒(méi)有看到它指定任何模型或者表名,那么它是在哪里找到這個(gè)表的呢惰说。我們先看getTable()方法磨德,這個(gè)方法就定義在下方,

/**
     * Begin a new database query against the table.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function getTable()
    {
        return $this->connection->table($this->table);
    }

    /**
     * Get the database connection instance.
     *
     * @return \Illuminate\Database\ConnectionInterface
     */
    public function getConnection()
    {
        return $this->connection;
    }

我特意把getConnection()方法一起粘過(guò)來(lái)吆视。我們發(fā)現(xiàn)這兩個(gè)方法都返回了該類的屬性典挑,那么我們就去構(gòu)造方法中找傳入的依賴。

  /**
     * Create a new token repository instance.
     *
     * @param  \Illuminate\Database\ConnectionInterface  $connection
     * @param  string  $table
     * @param  string  $hashKey
     * @param  int  $expires
     * @return void
     */
    public function __construct(ConnectionInterface $connection, $table, $hashKey, $expires = 60)
    {
        $this->table = $table;
        $this->hashKey = $hashKey;
        $this->expires = $expires * 60;
        $this->connection = $connection;
    }

構(gòu)造方法中果然看到傳入了一個(gè)table和一個(gè)connection啦吧,connection我們不管您觉,是數(shù)據(jù)庫(kù)連接實(shí)例,我們找table授滓,可是我們發(fā)現(xiàn)這里table就是一個(gè)簡(jiǎn)單的string類型琳水,那么我們必須找到這個(gè)DatabaseTokenRepository類實(shí)例化的地方,繼續(xù)搜索褒墨。我們找到了一個(gè)PasswordBrokerManager類里面createTokenRepository方法返回了DatabaseTokenRepository的實(shí)例對(duì)象炫刷。

/**
     * Create a token repository instance based on the given configuration.
     *
     * @param  array  $config
     * @return \Illuminate\Auth\Passwords\TokenRepositoryInterface
     */
    protected function createTokenRepository(array $config)
    {
        $key = $this->app['config']['app.key'];

        if (Str::startsWith($key, 'base64:')) {
            $key = base64_decode(substr($key, 7));
        }

        $connection = isset($config['connection']) ? $config['connection'] : null;

        return new DatabaseTokenRepository(
            $this->app['db']->connection($connection),
            $config['table'],
            $key,
            $config['expire']
        );
    }

我們發(fā)現(xiàn)這里傳入了config數(shù)組里的table擎宝,
我們?cè)赾onfig\auth.php中找到了配置項(xiàng)

 'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

為什么實(shí)例對(duì)象要在這里返回呢郁妈,到這里我推測(cè)再搜索PasswordBrokerManager被引用的地方一定是一個(gè)服務(wù)提供者了。果然我們找到了Illuminate\Auth\Passwords\PasswordResetServiceProvider.php
服務(wù)容器通過(guò)這個(gè)服務(wù)提供者調(diào)用PasswordBrokerManager從而解析出DatabaseTokenRepository實(shí)例绍申,提供依賴注入噩咪。

好現(xiàn)在我們?cè)倩氐?/p>

 $user->sendPasswordResetNotification(
            $this->tokens->create($user)
        );

我們要來(lái)看sendPasswordResetNotification顾彰,直接搜索到定義處Illuminate\Auth\Passwords\CanResetPassword.php

   /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }

我們追蹤notify方法,notifyIlluminate\Notifications\RoutesNotifications.php的traitRoutesNotifications中定義胃碾。然后Illuminate\Notifications\Notifiable.phptrait中引用了RoutesNotifications涨享,最后在app\User.php中引用了Notifiable。上面說(shuō)到過(guò)Illuminate\Foundation\Auth\User.php實(shí)現(xiàn)了CanResetPassword接口仆百,app\User.php中的User其實(shí)就是繼承了Illuminate\Foundation\Auth\User.php

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
     ... ...
}

所以上面的

$this->notify(new ResetPasswordNotification($token));

中的this就是User實(shí)例厕隧,所以能夠調(diào)用notify方法,這里繼承實(shí)現(xiàn)關(guān)系比較復(fù)雜俄周,需要自己多鉆研吁讨。剩下就不再挖掘了,這里是通過(guò)laravel框架的通知來(lái)發(fā)送重置密碼的郵件峦朗。

篇幅原因只能寫到這里建丧,繼續(xù)深挖下去是無(wú)窮無(wú)盡的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末波势,一起剝皮案震驚了整個(gè)濱河市翎朱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尺铣,老刑警劉巖拴曲,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異迄埃,居然都是意外死亡疗韵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侄非,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕉汪,“玉大人,你說(shuō)我怎么就攤上這事逞怨≌甙蹋” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵叠赦,是天一觀的道長(zhǎng)驹马。 經(jīng)常有香客問(wèn)我,道長(zhǎng)除秀,這世上最難降的妖魔是什么糯累? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮册踩,結(jié)果婚禮上泳姐,老公的妹妹穿的比我還像新娘。我一直安慰自己暂吉,他們只是感情好胖秒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布缎患。 她就那樣靜靜地躺著,像睡著了一般阎肝。 火紅的嫁衣襯著肌膚如雪挤渔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天风题,我揣著相機(jī)與錄音判导,去河邊找鬼。 笑死沛硅,一個(gè)胖子當(dāng)著我的面吹牛骡楼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稽鞭,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鸟整,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了朦蕴?” 一聲冷哼從身側(cè)響起篮条,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吩抓,沒(méi)想到半個(gè)月后涉茧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疹娶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年伴栓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雨饺。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钳垮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出额港,到底是詐尸還是另有隱情饺窿,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布移斩,位于F島的核電站肚医,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏向瓷。R本人自食惡果不足惜肠套,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望猖任。 院中可真熱鬧你稚,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)室琢。三九已至乾闰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盈滴,已是汗流浹背涯肩。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巢钓,地道東北人病苗。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像症汹,于是被迫代替她去往敵國(guó)和親硫朦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 先說(shuō)幾句廢話背镇,調(diào)和氣氛咬展。事情的起由來(lái)自客戶需求頻繁變更,偉大的師傅決定橫刀立馬的改革使用新的框架(created ...
    wsdadan閱讀 3,054評(píng)論 0 12
  • 簡(jiǎn)介 laravel 使實(shí)施認(rèn)證的變得非常簡(jiǎn)單瞒斩,事實(shí)上破婆,它提供了非常全面的配置項(xiàng)以適應(yīng)應(yīng)用的業(yè)務(wù)。認(rèn)證的配置文件存...
    Dearmadman閱讀 6,136評(píng)論 2 13
  • 過(guò)去做事情急胸囱,什么東西拿起來(lái)就用祷舀,不喜歡進(jìn)行系統(tǒng)性的學(xué)習(xí),造成在使用過(guò)程中的錯(cuò)誤和低效烹笔,現(xiàn)在感覺自己耐心多了裳扯,用之...
    馬文Marvin閱讀 1,985評(píng)論 0 10
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)谤职,斷路器嚎朽,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 2016-08-17 補(bǔ)充 Exception 部分改造方案的內(nèi)容2016-08-13 補(bǔ)充 View 部分改造方...
    haolisand閱讀 5,296評(píng)論 0 16