iT邦幫忙

1

請教如何修改 LARAVEL內建 忘記密碼功能

2019/02/12 更新

我目前找到核心位置是在Illuminate\Auth\Passwords\DatabaseTokenRepository

目前用下方連結這個方式去做繼承以及覆蓋方法,但是目前卡在答案的 For Laravel 5.2 第4步驟

Overwrite the resolve() method to return a new PasswordBroker with your token repository from step 1

https://stackoverflow.com/questions/40483634/password-reset-table-column-name-override-laravel-5

請教各位大大 這個步驟我該怎麼修改才能運作(我使用 5.5版本)


LARAVEL原本由內建的忘記密碼功能,只需要輸入email即可以傳送忘記密碼連結。
但目前系統需求。
有可能 兩個帳號 但 EMAIL是一樣的,
所以我做了必須去查詢資料表兩個皆符合才會產生token並寄送email

但 password_resets 資料表只有 三個欄位 email 以及 token 和 建立時間 三欄位,所以沒有紀錄 使用者帳號

會有個問題,兩個帳號但email相同

雖然申請 token時 可能用 A帳號 和 email 申請token

但修改密碼時 可以 B帳號 和 email 修改 B帳號的密碼

想請教各位大大!laravel 寫入token的位置在哪邊?

0
浩瀚星空
iT邦大師 1 級 ‧ 2019-02-11 17:56:45

我覺得,你是否找錯方向了。
你找token位置作什麼。

再加上,你居然不知道你的token放在哪?
不是在程式宣告時,就會要求你的資料庫欄位建立了?

還是你想要找其模組的程式位置。
基本而言並不太建議直接改核心模組處理。
最好還是用繼承的方式來處理會比較安全。

正常來說,帳號也最好是email唯一性,如果有非唯一性的需求。
那在忘記密碼操作,就得要多一個值來處理。

依照你目前的情況,除了email之外,還需要帳號做為條件才行。
畢竟你的email並不是唯一性,這是一個麻煩。

Victor iT邦新手 4 級 ‧ 2019-02-12 09:47:11 檢舉

yoching 大大你好!確實有Email 非唯一性的需求,
我目前只有在要生產token時會去判斷 user 資料表有沒有 帳號與Email 皆符合的會員,若有才會產生token 並發送email 給該使用者

但是因為「內建」的功能只有 在password_resets 資料表中記錄 token、email、created_at 三個欄位

我是想請問該如何去做如你所說的繼承的動作,
昨天我有找到laravel 框架把這部分寫在哪裡,但想請教大大該如何去做繼承。

原則上還是並不太建議改核心。

不過對於laravel我並不是很熟。但依照所有的框架特性而言。
都因該會有核心擴展的方式處理。
沒有的話,也可以自行去設計一個clasee來去繼承原password物件後。再自行加入自已的處理模式。

真沒辦法不懂到如何去做擴展的話。你就直接改核心吧。
不建議改核心的因素是,框架會有更新版本。如哪天不小心忘了或是有其它人手賤,將它更新版本的話。你所改的核心就又會被還原了。
(至少我自已就笨笨的發生過,差點沒被自已給氣死)

0
淺水員
iT邦新手 2 級 ‧ 2019-02-13 09:31:54

看到這串,我這幾天才開始去看 laravel 的 auth
目前我的想法是自己處理忘記密碼的流程
因為他忘記密碼的 controller 本來就是跟身分驗證分開的
大致上如下:

  1. view 那邊忘記密碼的連結改為自訂的 url。
  2. route 那邊新增這個 url 的連結,並交給自訂的 controller 處理
  3. 至於 controller 可能會用到 Illuminate\Support\Facades\Auth 來判斷登入狀態,此外相關的 model 也要自己建立。

當使用者透過表單發送忘記密碼所需的資料:

  1. view 的表單應該要傳送 name、email
  2. controller 收到後,先用 $request->validate 去驗證,然後透過 model 找尋 users 表中同時符合 name 跟 email 的 id
  3. 產生 token ,紀錄 userId、token、timestamp 於表 costom_reset_pwd
  4. 發送連結到對方的信箱
  5. 回應 view:信件已寄出...

當使用者點選 email 的連結:

  1. route::get 收到請求轉給 controller
  2. 用 $request->validate 去驗證 token 後,透過 model 查詢 costom_reset_pwd 表中的 userId 跟 timestamp
  3. 如果 timestamp 過期則回傳錯誤,否則就把使用者設為登入,刪除 costom_reset_pwd 中該使用者的 token,並回傳「重設密碼」的頁面。
0
Lucas
iT邦新手 5 級 ‧ 2019-10-05 00:46:26

不知道您現在解決了沒有...,沒關係,我還是留下解答,可供參考。


版本: Laravel 5.5

資料表結構

假定你 User 模型帳號欄位為 username,因此重設密碼資料表也加此欄位。

database/migrations/2014_10_12_100000_create_password_resets_table.php

...
        Schema::create('password_resets', function (Blueprint $table) {
            $table->string('username')->index();
            $table->string('email');
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
...

覆寫密碼重設功能

開始覆寫 Laravel 預設的密碼重設 Controller

app/Http/Controllers/Auth/ForgotPasswordController.php

...
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
    ...

    public function sendResetLinkEmail(Request $request)
    {
        $this->validateEmail($request);

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

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

    protected function validateEmail(Request $request)
    {
        $this->validate($request, [
            'username' => 'required',
            'email' => 'required|email',
        ]);
    }
}

app/Http/Controllers/Auth/PasswordController.php

...
use Illuminate\Http\Request;

class PasswordController extends Controller
{
    ...

    public function showResetForm(Request $request, $token = null)
    {
        return view('auth.passwords.reset')->with([
            'token' => $token,
            'username' => $request->username,
            'email' => $request->email,
        ]);
    }

    protected function credentials(Request $request)
    {
        return $request->only(
            'username', 'email', 'password', 'password_confirmation', 'token'
        );
    }

    protected function rules()
    {
        return [
            'token' => 'required',
            'username' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed|min:6',
        ];
    }

    protected function sendResetFailedResponse(Request $request, $response)
    {
        return redirect()->back()
                    ->withInput($request->only('username', 'email'))
                    ->withErrors(['email' => trans($response)]);
    }
}

新增相關須覆寫的檔案

PasswordBrokerManager 是生產 PasswordBroker 的 class

app/Passwords/PasswordBrokerManager.php

<?php

namespace App\Passwords;

use Illuminate\Auth\Passwords\PasswordBrokerManager as BasePasswordBrokerManager;
use Illuminate\Support\Str;

class PasswordBrokerManager extends BasePasswordBrokerManager
{
    protected function createTokenRepository(array $config)
    {
        $key = $this->app['config']['app.key'];

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

        $connection = $config['connection'] ?? null;

        // 讀取複寫的 DatabaseTokenRepository
        return new DatabaseTokenRepository(
            $this->app['db']->connection($connection),
            $this->app['hash'],
            $config['table'],
            $key,
            $config['expire']
        );
    }
}

DatabaseTokenRepository 是管重設密碼資料表跟 Token 的 class

app/Passwords/DatabaseTokenRepository.php

<?php

namespace App\Passwords;

use App\User;
use Carbon\Carbon;
use Illuminate\Auth\Passwords\DatabaseTokenRepository as BaseDatabaseTokenRepository;

class DatabaseTokenRepository extends BaseDatabaseTokenRepository
{
    public function create(User $user)
    {
        $this->deleteExisting($user);

        $token = $this->createNewToken();

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

        return $token;
    }

    protected function deleteExisting(User $user)
    {
        return $this->getTable()->where('username', $user->username)->delete();
    }

    protected function getPayload($username, $email, $token)
    {
        return [
            'username' => $username,
            'email' => $email,
            'token' => $this->hasher->make($token),
            'created_at' => new Carbon,
        ];
    }

    public function exists(User $user, $token)
    {
        $record = (array) $this->getTable()->where(
            'username', $user->username
        )->first();

        return $record &&
               ! $this->tokenExpired($record['created_at']) &&
                 $this->hasher->check($token, $record['token']);
    }
}

最後,註冊新的 PasswordBrokerManager

app/Providers/AppServiceProvider.php

...
use App\Passwords\PasswordBrokerManager;

class AppServiceProvider extends ServiceProvider
{
    ...
    public function register()
    {
        $this->app->singleton('auth.password', function ($app) {
            return new PasswordBrokerManager($app);
        });
    }
}

我要發表回答

立即登入回答