Laavel의 PasswordBroker 조사

안녕하세요.나는 파카이다.
이번에는 라벨의 5.3부터 조금 구조적으로 들어간 패스워드브로커를 소스의 수준으로 조사했다.

버전 확인


Laavel8의 PasswordBroker 설치가 확인되었습니다.
실제 설치와 5 계열 때는 큰 변화가 없었다.

도대체 PasswordBroker가 뭐예요?


매번 라벨의 이름은 일본인들이 이해하기 어려워요...
브로커는 중개인이라는 뜻이 있는 것 같아요.실제 코드를 읽으면 이해하기 쉬우나 비밀번호 리셋 처리의 절차는 이PasswordBroker를 통해 진행된다.
다음은 PasswordBroker가 할 일입니다.
  • 비밀번호 재설정 링크 알림을 보내는 중재
  • 비밀번호 재설정
  • 비밀번호 리셋 영패의 검증, 시간 간격
  • 영패 창설 중재
  • 그렇습니다.

    PasswordBroker 만드는 법

    PasswordBrokerPasswordResetServiceProviderPasswordManagerPasswordBroker 느낌으로 3개 반을 통해 제작됐다.PasswordBroker 구조기로서 두 개의 매개 변수가 있다.
    /**
     * Create a new password broker instance.
     *
     * @param  \Illuminate\Auth\Passwords\TokenRepositoryInterface  $tokens
     * @param  \Illuminate\Contracts\Auth\UserProvider  $users
     * @return void
     */
    public function __construct(TokenRepositoryInterface $tokens,
                             UserProvider $users)
    {
        $this->users = $users;
        $this->tokens = $tokens;
    }
    
    UserProvider는 Auth에서도 자주 사용하는 물건이다.TokenRepositoryInterface 이름처럼 비밀번호 토큰의 창고죠.
    현재DatabaseTokenRepository 실현TokenRepositoryInterface.(이 Token Repository Interface는 흔히 볼 수 있는 창고 모델로서의 실현은 참고 가치가 있다고 생각한다.)

    PasswordBrokerManager의 역할

    PasswordBrokerManager의 역할은 명칭과 같이 관리이다.
    여러 브로커를 관리할 수 있습니다. 호출할 때 생성되지 않으면 config에 따라 실례를 생성하고 되돌려줍니다.
    흔한 실크룩입니다.
    public function broker($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();
    
        return $this->brokers[$name] ?? ($this->brokers[$name] = $this->resolve($name));
    }
    

    Token 만들기


    우선 영패의 기초 제작을 살펴보자.
    다음은 PasswordBroker의 방법입니다.
    /**
     * Create a new password reset token for the given user.
     *
     * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
     * @return string
     */
    public function createToken(CanResetPasswordContract $user)
    {
        return $this->tokens->create($user);
    }
    
    Token Repository Interface라는 create.
    실제로 DatabaseToken Repository는 이렇습니다.
    /**
     * Create a new token record.
     *
     * @param  \Illuminate\Contracts\Auth\CanResetPassword  $user
     * @return string
     */
    public function create(CanResetPasswordContract $user)
    {
        // パスワードリセットに利用するメールアドレスを取得
        $email = $user->getEmailForPasswordReset();
    
        // tokenが入ってるテーブルに対象ユーザ(email)のデータがあったら消す
        $this->deleteExisting($user);
    
        // ランダムなトークンを生成します。
        $token = $this->createNewToken();
    
        $this->getTable()->insert($this->getPayload($email, $token));
    
        return $token;
    }
    
    그럼 삽입할 때getPayload 방법을 살펴봅시다.
    /**
     * Build the record payload for the table.
     *
     * @param  string  $email
     * @param  string  $token
     * @return array
     */
    protected function getPayload($email, $token)
    {
        return ['email' => $email, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
    }
    
    토큰을 산열하여 넣습니다.안전의식이 있는 실장입니다.
    실제 DB를 볼 수 있는 사람도 남용하지 않도록 데이터를 산열했다.
    암호를 재설정할 때 원본 영패를 링크에 끼워 넣고 산열하여 비교 검사를 합니다.

    PasswordBroker:sendResetLink 처리


    가장 많이 사용할 수 있는sendResetLink 처리를 살펴보겠습니다.
    /**
     * Send a password reset link to a user.
     *
     * @param  array  $credentials
     * @param  \Closure|null  $callback
     * @return string
     */
    public function sendResetLink(array $credentials, Closure $callback = null)
    {
        // ユーザを取得
        $user = $this->getUser($credentials);
    
        if (is_null($user)) {
            return static::INVALID_USER;
        }
    
        // 最近作られたトークンがあるか(Throttleに引っかかるか)
        if ($this->tokens->recentlyCreatedToken($user)) {
            return static::RESET_THROTTLED;
        }
    
        $token = $this->tokens->create($user);
    
        if ($callback) {
            $callback($user, $token);
        } else {
            // 通知を発火する
            $user->sendPasswordResetNotification($token);
        }
    
        return static::RESET_LINK_SENT;
    }
    

    getUser 처리


    중요한 것은 이 두 줄이다.
    // 受け取ったcredentialsからtokenを除く(理由はfindに引っかからない可能性があるため)
    $credentials = Arr::except($credentials, ['token']);
    
    // 主にemail情報を元にUser情報を取得する
    $user = $this->users->retrieveByCredentials($credentials);
    
    // あとは存在しなかった場合のエラー処理、返り値はuser。
    

    $this->tokens->recentlyCreatedToken의 처리


    나는 처음에는 이 처리를 잘 몰랐다.
    간단하게 설명하자면throttling(기본 60초)을 설정하면 이전에 작성한 기록追加日時+throttling秒現在日時을 비교한다.
    public function recentlyCreatedToken(CanResetPasswordContract $user)
    {
        $record = (array) $this->getTable()->where(
            'email', $user->getEmailForPasswordReset()
        )->first();
    
        return $record && $this->tokenRecentlyCreated($record['created_at']);
    }
    
    protected function tokenRecentlyCreated($createdAt)
    {
        if ($this->throttle <= 0) {
            return false;
        }
    
        return Carbon::parse($createdAt)->addSeconds(
            $this->throttle
        )->isFuture(); // isFutureは未来かどうか
    }
    
    즉 이 처리가 설정된throttle의 초 이내에 호출되었는지 판단하는 것이다.
    결과는 다음과 같은 처리에서 탄착되었다.
    if ($this->tokens->recentlyCreatedToken($user)) {
        return static::RESET_THROTTLED;
    }
    

    처리 방법


    마지막으로 메일만 보냈어요.Illuminate\Auth\Passwords\CanResetPassword TRAIT를 사용하는 경우 다음 절차를 수행합니다.
    이것에 관해서는 사용자 클래스에서 단독으로 실현할 수 있다.
    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }
    
    이 notify 방법은trait의 방법이다.
    저라면 모델 내Notifiable에 TRAIT를 사용했기 때문notify 방법RoutesNotifications TRAIT 방법입니다.sendPasswordResetNotification 메서드도 Illuminate\Auth\Passwords\CanResetPassword TRAIT에서 호출된 경우CanResetPassword::sendResetNotificationUserクラスNotifiableトレイトRoutesNotifications::notify전화번호가 어떻게 되세요?
    public function notify($instance) // ResetPasswordNotificationインスタンスが入ってくる
    {
        app(Dispatcher::class)->send($this, $instance);
    }
    
    여기서부터는 일반 노티픽션과 같다.ResetPasswordNotification클래스에 toMail방법이 설치되어 있어 메일 발송 방법으로 호출되었습니다.

    PasswordBroker:sendResetLink 반환 값 정보


    반환된 값은 상태입니다.
    실제 사용하는 코드는 다음과 같은 느낌을 가지고 있다.
    return $status == Password::RESET_LINK_SENT
        ? app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status])
        : app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
    

    총결산


    이렇게 하면 PasswordBroker가 완성됩니다.
    잘 처리한 게 Token Repository인 것 같은데.hasher를 통해 throttle, token을 DB에 넣는 것 같은 거.
    그럼 안녕히 계세요.

    좋은 웹페이지 즐겨찾기