php mysql中的简单OTP
#网络开发人员 #教程 #php #mysql

(1)数据库

CREATE TABLE `otp` (
  `email` varchar(255) NOT NULL,
  `pass` varchar(255) NOT NULL,
  `timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `otp`
  ADD PRIMARY KEY (`_email`);
  • email用户的电子邮件,主键。您也可以将其与订单,交易或任何要添加otp的订单,交易。
  • pass otp本身。
  • 创建OTP时的timestamp时间戳。用于预防到期和蛮力。

(2)PHP库

<?php
class OTP {
  // (A) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error = "";
  function __construct() {
    $this->pdo = new PDO(
      "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
      DB_USER, DB_PASSWORD, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
  }

  // (B) DESTRUCTOR - CLOSE CONNECTION
  function __destruct() {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }

  // (C) HELPER - RUN SQL QUERY
  function query ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }

  // (D) GENERATE OTP
  function generate ($email) {
    // (D1) CHECK EXISTING OTP REQUEST
    $this->query("SELECT * FROM `otp` WHERE `email`=?", [$email]);
    $otp = $this->stmt->fetch();
    if (is_array($otp) && (strtotime("now") < strtotime($otp["timestamp"]) + (OTP_VALID * 60))) {
      $this->error = "You already have a pending OTP.";
      return false;
    }

    // (D2) CREATE RANDOM OTP
    $alphabets = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
    $count = strlen($alphabets) - 1;
    $pass = "";
    for ($i=0; $i<OTP_LEN; $i++) { $pass .= $alphabets[rand(0, $count)]; }
    $this->query(
      "REPLACE INTO `otp` (`email`, `pass`) VALUES (?,?)",
      [$email, password_hash($pass, PASSWORD_DEFAULT)]
    );

    // (D3) SEND VIA EMAIL
    if (@mail($email, "Your OTP",
      "Your OTP is $pass. Enter at <a href='http://localhost/3b-challenge.php'>SITE</a>.",
      implode("\r\n", ["MIME-Version: 1.0", "Content-type: text/html; charset=utf-8"])
    )) { return true; }
    else {
      $this->error = "Failed to send OTP email.";
      return false;
    }
  }

  // (E) CHALLENGE OTP
  function challenge ($email, $pass) {
    // (E1) GET THE OTP ENTRY
    $this->query("SELECT * FROM `otp` WHERE `email`=?", [$email]);
    $otp = $this->stmt->fetch();

    // (E2) CHECK - NOT FOUND
    if (!is_array($otp)) {
      $this->error = "The specified OTP request is not found.";
      return false;
    }

    // (E3) CHECK - EXPIRED
    if (strtotime("now") > strtotime($otp["timestamp"]) + (OTP_VALID * 60)) {
      $this->error = "OTP has expired.";
      return false;
    }

    // (E4) CHECK - INCORRECT PASSWORD
    if (!password_verify($pass, $otp["pass"])) {
      $this->error = "Incorrect OTP.";
      return false;
    }

    // (E5) OK - DELETE OTP REQUEST
    $this->query("DELETE FROM `otp` WHERE `email`=?", [$email]);
    return true;
  }
}

// (F) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");

// (G) OTP SETTINGS
define("OTP_VALID", "15"); // otp valid for n minutes
define("OTP_LEN", "8");    // otp length

// (H) NEW OTP OBJECT
$_OTP = new OTP();
  • (a,b,h)创建$_OTP = new OTP()时,构造函数将连接到数据库。破坏者关闭连接。
  • (c)运行SQL查询的辅助功能。
  • (F&G)数据库和OTP设置 - 更改为您自己的。
  • (d)OTP进程的步骤1,用户请求OTP。几乎“生成一个随机字符串,保存到数据库中,并通过电子邮件将其发送给用户”。
  • (e)OTP进程的步骤2,用户单击电子邮件中的链接并输入OTP。然后,此函数将针对数据库验证输入的OTP。

(3)HTML页面

<!-- (A) OTP REQUEST FORM -->
<form method="post" target="_self">
  <label>Email</label>
  <input type="email" name="email" required value="jon@doe.com">
  <input type="submit" value="Request OTP">
</form>

<?php
// (B) PROCESS OTP REQUEST
if (isset($_POST["email"])) {
  require "2-otp.php";
  $pass = $_OTP->generate($_POST["email"]);
  echo $pass ? "<div class='note'>OTP SENT.</div>" : "<div class='note'>".$_OTP->error."</div>" ;
}
?>

这只是一个虚拟形式。在您自己的项目中,使用$_OTP->generate(EMAIL)创建OTP并将其发送给用户。

<!-- (A) OTP CHALLENGE FORM -->
<form method="post" target="_self">
  <label>Email</label>
  <input type="email" name="email" required value="jon@doe.com">
  <label>OTP</label>
  <input type="password" name="otp" required>
  <input type="submit" value="Go">
</form>

<?php
// (B) PROCESS OTP CHALLENGE
if (isset($_POST["email"])) {
  require "2-otp.php";
  $pass = $_OTP->challenge($_POST["email"], $_POST["otp"]);
  // @TODO - DO SOMETHING ON VERIFIED
  echo $pass ? "<div class='note'>OTP VERIFIED.</div>" : "<div class='note'>".$_OTP->error."</div>" ;
}
?>
  • 用户点击电子邮件中的链接并在此页面上降落。
  • 输入OTP,$_OTP->challenge()将进行验证。
  • 此后,请做任何需要的事情。

改进

这只是一个简化的示例,可以帮助初学者掌握概念和过程流。最低限度,我建议:

  • 强制使用https。
  • tries添加到otp表中。
  • 修改function challenge ()-在每个错误的密码尝试中,tries + 1
  • 做一些if (tries >= N)。锁定用户的帐户,冻结交易,需要手动管理干预,暂停一定时间等...

结束

这就是这个冷凝的教程。这是要点的链接,等等。

Gist | Simple PHP MYSQL OTP - Code Boxx