[Harson] PHP를 사용하여 간단한 Remember Me(자동 로그인) 구현

56475 단어 PHPtech

개시하다


주변에서 몰랐던 열등감을 인증하기 위해 아래 글 두 편에 이어 리멤버미(자동 로그인 기능)를 시행해 봤다.
https://zenn.dev/syamozipc/articles/php_auth_register
https://zenn.dev/syamozipc/articles/php_password_reset
누구한테 도움이 됐으면 좋겠어요.

실장 프로세스

  • 로그인 형식에 로그인 정보 저장 확인란 설정
  • 메일 주소와 비밀번호가users표의 기록과 일치하면 이 사용자로 로그인
  • 스토리지 로그인 정보를 확인하면 다음과 같이 처리됩니다.
  • 랜덤 문자열의 영패를 생성하고 쿠키에서remembertoken으로 저장
  • 상기 영패를 암호화한 클라이언트 테이블의remembertoken 열에 저장
  • 내 페이지를 방문할 때 다음과 같은 로그인 검사
  • 세션에 사용자 ID가 있으면 로그인한 것으로 내 페이지를 표시
  • 사용자 ID는 없지만 쿠키에서 remember만약token이 있다면, 그 값을users표의remember로 합니다token의 사용자로 로그인하여 내 페이지로 들어가기
  • 둘 다 해당되지 않으면 로그인 양식이 로그인되지 않은 것으로 표시
  • 내 페이지의 로그아웃 단추를 눌렀을 때users표의rememberToken 열을 NULL로 업데이트하고 로그아웃
  • 파일 구성


    .
    ├─ database.php
    ├─ login.php
    ├─ logout.php
    ├─ show_login_form.php
    ├─ show_mypage.php
    └─ views
        ├── login_form.php
        └── mypage.php
    

    사용할 테이블


    사용자 테이블


    열 이름
    시험을 준비하다
    id
    주 키워드
    email
    register_token
    가등기 때 사용한다.고유 식별 사용자
    register_token_sent_at
    임시 등록에 사용되는 유효기간 관리
    register_token_verified_at
    등록 시 업데이트
    password
    remember_token
    remember me를 구현하는 데 사용됩니다.고유 식별 사용자
    status
    num형.tentative: 임시 등록,puvlic:본 로그인
    created_at
    updated_at
    제작된 DDL은 여기(MySQL로 가정)
    CREATE TABLE `users` (
        `id` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
        `email` VARCHAR(50) UNIQUE NOT NULL,
        `register_token` VARCHAR(80),
        `register_token_sent_at` DATETIME,
        `register_token_verified_at` DATETIME,
        `password` VARCHAR(80),
        `remember_token` VARCHAR(80),
        `status` ENUM('tentative', 'public') NOT NULL DEFAULT 'tentative',
        `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP  ON UPDATE CURRENT_TIMESTAMP
    );
    
    !
    register_token、register_token_sent_at、register_token_verified_at,status는 새로운 로그인 기능을 실시하는 글에서 사용하기 때문에 이번 실시에 참여하지 않습니다.
    !
    전제는 한 사람도 users 테이블에 등록할 수 있다는 것이다.
    만약 누군가가 자신의 환경에서 함께 읽어보려고 한다면, 나는 SQL로 유입될 준비를 할 것이다.
    로그인 암호는 "password"입니다.
    유입용 SQL
    INSERT INTO `users` (`id`, `email`, `register_token`, `register_token_sent_at`, `register_token_verified_at`, `password`, `status`, `created_at`, `updated_at`)
    VALUES
    	(1,'[email protected]','45085a3ce32c6623c4159dc3e202007c6ef26d0a8201ab9802426a35a0bc2474','2022-03-27 12:21:41','2022-03-27 12:21:58','$2y$10$BmxkPc6uMmRyWvgvpUUs1OOlSuplcyLb6FzQEooh1LHS/aPUt6no2','public','2022-03-27 12:21:41','2022-03-27 16:38:58');
    

    1. 메일 주소와 비밀번호가users표의 기록에 부합되면 해당 사용자로 로그인


    show_login_form.php
    <?php
    session_start();
    
    // formに埋め込むcsrf tokenの生成
    if (empty($_SESSION['_csrf_token'])) {
        $_SESSION['_csrf_token'] = bin2hex(random_bytes(32));
    }
    
    // 本登録フォームを読み込む
    require_once './views/login_form.php';
    
    창 부분 (form 탭에서 선택)
    views/login_form.php
    <p>ログイン</p>
    <form action="login.php" method="POST">
        <input type="hidden" name="_csrf_token" value="<?= $_SESSION['_csrf_token']; ?>">
        <label>メールアドレス
            <input type="email" name="email">
        </label>
        <br>
        <label>パスワード
            <input type="password" name="password">
        </label>
        <br>
        <label>ログイン情報を記憶する
            <input type="checkbox" name="remember_me">
        </label>
        <br>
        <button type="submit">ログイン</button>
    </form>
    

    2. 만약에 메일 주소가 users표에 로그인되면 메일 발송 완료 화면과 종료를 표시합니다


    login.php
    <?php
    session_start();
    
    require_once './database.php';
    $pdo = getPdo();
    
    $request = filter_input_array(INPUT_POST);
    
    // csrf tokenが正しければOK
    if (
        empty($request['_csrf_token'])
        || empty($_SESSION['_csrf_token'])
        || $request['_csrf_token'] !== $_SESSION['_csrf_token']
    ) {
        exit('不正なリクエストです');
    }
    
    // 本来はここでメールアドレスとパスワードのバリデーションをする
    
    // 入力されたメールアドレスに合致するユーザーを取得
    $sql = 'SELECT * FROM users WHERE `email` = :email AND `status` = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':email', $request['email'], \PDO::PARAM_STR);
    $stmt->bindValue(':status', 'public', \PDO::PARAM_STR);
    $stmt->execute();
    $user = $stmt->fetch(\PDO::FETCH_OBJ);
    
    // 「メールアドレスが間違っている」のようにどちらが間違っているのか表示すると、
    // 別人のアカウントでログインしようとする悪意あるユーザーに、
    // 「メールアドレスが間違っている」「パスワードは間違っていない」という情報を与えてしまうので、明示しない
    if (
        !$user
        || !password_verify($request['password'], $user->password)
    ) {
        exit('登録情報が間違っています。');
    }
    
    // セッションが有効な間はログイン済みとなる
    $_SESSION['user_id'] = $user->id;
    
    // 「ログイン情報を記憶する」をチェックしていなければ、このままマイページへ
    if (empty($request['remember_me'])) {
        header('Location: ./show_mypage.php');
        exit();
    }
    
    코드에 나오는 데이터 베이스.php의 내용은 여기에 있습니다.
    database.php
    database.php
    <?php
    
    function getPdo()
    {
        $dsn = 'mysql:host=localhost;dbname=zenn;charset=utf8mb4';
        $options = [
            \PDO::ATTR_PERSISTENT => true,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        ];
    
        try {
            return new \PDO($dsn, 'root', 'root', $options);
        } catch (\PDOException $e) {
            exit($e->getMessage());
        }
    }
    

    3. 스토리지 로그인 정보를 검토한 경우 Remember Me(자동 로그인) 처리


    login.php(※ 2. 파일의 계속)
    // ランダムな文字列のtokenを生成
    $rememberToken = bin2hex(random_bytes(32));
    
    // 暗号化したremember tokenを保存
    $sql = 'UPDATE users SET `remember_token` = :remember_token WHERE `email` = :email';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':remember_token', md5($rememberToken), \PDO::PARAM_STR);
    $stmt->bindValue(':email', $request['email'], \PDO::PARAM_STR);
    $stmt->execute();
    
    // cookieのオプション
    $options = [
        'expires' => time() + 60 * 60 * 24 * 365, // cookieの有効期限を1年間に設定
        'path' => '/', // 有効範囲を「ドメイン配下全て」に設定
        'httponly' => true // HTTPを通してのみcookieにアクセス可能(JavaScriptからのアクセスは不可となる)
    ];
    
    setcookie('remember_token', $rememberToken, $options);
    
    header('Location: ./show_mypage.php');
    exit();
    

    4. 내 페이지를 방문할 때 두 가지 로그인을 확인하고 내 페이지를 표시한다


    show_mypage.php(※ 3. 파일의 계속)
    <?php
    session_start();
    
    require_once './database.php';
    $pdo = getPdo();
    
    // (1) セッションにユーザーIDが保存されている場合
    if (isset($_SESSION['user_id'])) {
        // セッションに保存されたユーザーIDに合致するユーザーを取得
        $sql = 'SELECT * FROM users WHERE `id` = :id AND `status` = :status';
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(':id', $_SESSION['user_id'], \PDO::PARAM_INT);
        $stmt->bindValue(':status', 'public', \PDO::PARAM_STR);
        $stmt->execute();
        $user = $stmt->fetch(\PDO::FETCH_OBJ);
    
    // (2) セッションのユーザーIDは破棄されているが、cookieにremember_tokenが保存されている場合
    } else if (isset($_COOKIE['remember_token'])) {
        // usersテーブルに保存されている remember_token は md5() で暗号化されているので、同様に暗号化した token をWHERE句に指定する
        $sql = 'SELECT * FROM users WHERE `remember_token` = :remember_token AND `status` = :status';
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(':remember_token', md5($_COOKIE['remember_token']), \PDO::PARAM_STR);
        $stmt->bindValue(':status', 'public', \PDO::PARAM_STR);
        $stmt->execute();
        $user = $stmt->fetch(\PDO::FETCH_OBJ);
    }
    
    // ユーザーが一致しなければ、ログインフォームへ
    if (empty($user)) {
        header('Location: ./show_login_form.php');
        exit();
    }
    
    // ユーザーIDをセッションに保存してログイン済みとする
    $_SESSION['user_id'] = $user->id;
    
    // formに埋め込むcsrf tokenの生成
    if (empty($_SESSION['_csrf_token'])) {
        $_SESSION['_csrf_token'] = bin2hex(random_bytes(32));
    }
    
    require_once './views/mypage.php';
    
    내 페이지
    mypage.php
    <p><?= $user->email ?>さんのマイページ</p>
    <form action="logout.php" method="POST">
        <input type="hidden" name="_csrf_token" value="<?= $_SESSION['_csrf_token']; ?>">
        <button type="submit">ログアウトはこちら</button>
    </form>
    
    쿠키를 확인하려면 도구→Application 태그→Storage→Cookies를 확인하십시오.

    (1) 세션에 사용자 ID가 저장된 경우


    쿠키에 저장된 것은 세션 쿠키뿐입니다.
    먼저 브라우저를 닫고 브라우저를 다시 시작한 다음 제 페이지로 바로 들어가십시오.
    세션은 브라우저가 끝난 후에 버려집니다. 따라서 로그아웃 상태로 들어가 로그인 창을 표시합니다.

    (2) 세션의 사용자 ID가 폐기되었지만 remember cookietoken이 저장된 경우


    쿠키에 쿠키, 리멤버가 들어갔어요.token이 저장되었습니다.
    쿠키는 유효기간을 1년으로 설정하여 브라우저를 닫아도 폐기되지 않습니다.
    브라우저를 끝낸 후 다시 시작합니다. 내 페이지에 들어오면remmeber값이 일치하는 사용자로token에 로그인합니다.

    ※session_start를 발표했기 때문에 PHPSESSID 자체가 발매되었습니다.

    5. 내 페이지의 로그아웃 단추를 눌렀을 때users표의remembertoken 열을 NULL로 업데이트하고 로그아웃


    logout.php
    <?php
    session_start();
    
    require_once './database.php';
    $pdo = getPdo();
    
    $request = filter_input_array(INPUT_POST);
    
    // csrf tokenが正しければOK
    if (
        empty($request['_csrf_token'])
        || empty($_SESSION['_csrf_token'])
        || $request['_csrf_token'] !== $_SESSION['_csrf_token']
    ) {
        exit('不正なリクエストです');
    }
    
    // remember_tokenをNULLにする
    $sql = 'UPDATE users SET remember_token = NULL WHERE `id` = :id';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':id', $_SESSION['user_id'], \PDO::PARAM_INT);
    $stmt->execute();
    
    // remeber_tokenをcookieから削除
    setcookie('remember_token', '', time() - 6000, '/');
    
    // 以下、セッションの削除処理
    
    // セッション変数を初期化(メモリから削除するため)
    $_SESSION = [];
    
    // セッションクッキーを削除
    setcookie('PHPSESSID', '', time() - 6000, '/');
    
    // セッションファイル(セッションの実データ)を削除
    session_destroy();
    
    // ログインフォームへ
    header('Location: ./show_login_form.php');
    exit();
    
    끝까지 읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기