PHP library for Two Factor Authentication (TFA / 2FA)
文章目錄
安裝robthree/twofactorauth
composer require robthree/twofactorauth:1.8.2
2FA流程
啓用2FA
產生secret,驗證成功,同時產生一組恢復碼,儲入DB。
登入加入2FA驗證
Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php if (!defined('BASEPATH')) exit ('No direct script access allowed'); class Two_factor_auth_model extends CI_Model { public function __construct() { parent::__construct(); // 自定義參數 $issuer = 'JMTEC'; $digits = 6; // 驗證碼的位數 $period = 30; // 驗證碼的有效時間(秒) $algorithm = 'sha1'; // 使用的演算法 The algorithm used (one of `sha1`, `sha256`, `sha512`, `md5`) $qrcodeprovider = null; // 這裡以 null 為例,表示使用默認提供者 $rngprovider = null; // 使用默認的 RNG 提供者 $timeprovider = null; // 使用默認的時間提供者 // 使用自定義參數創建 TwoFactorAuth 對象 $this->tfa = new RobThree\Auth\TwoFactorAuth( $issuer, $digits, $period, $algorithm, $qrcodeprovider, $rngprovider, $timeprovider ); } public function createSecret() { return $this->tfa->createSecret(); } public function getQRCodeUrl($user, $secret) { return $this->tfa->getQRCodeImageAsDataUri($user, $secret); } public function verifyCode($secret, $code) { return $this->tfa->verifyCode($secret, $code); } } |
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<?php if (!defined('BASEPATH')) exit ('No direct script access allowed'); class TwoFactorAuth extends CI_Controller { public function __construct() { parent::__construct(); // 加載 TwoFactorAuth 模型 $this->load->model('two_factor_auth_model'); // 用戶模型來處理用戶數據 $this->load->model('user_model'); $this->load->model('recovery_code_model'); $this->load->library('form_validation'); } // 顯示二維碼給用戶 public function index() { // 假設用戶名從某處獲取,這裡為了案例直接寫死 $userId = 'email@xxx.test.xxx'; // $user->totp_secret = $this->two_factor_auth_model->createSecret(); // 生成秘鑰 $secret = $this->two_factor_auth_model->createSecret(); // 存儲秘鑰到 session,用於後續驗證 $this->session->set_userdata('two_factor_secret', $secret); // 獲取二維碼圖片 URL $data['qrCodeUrl'] = $this->two_factor_auth_model->getQRCodeUrl($userId, $secret); // 加載視圖,並傳遞二維碼圖片 URL $this->load->view('2fa_auth', $data); } // 驗證用戶輸入的代碼 public function verify() { $userId = 'email@xxx.test.xxx'; $this->form_validation->set_rules('code', 'Code', 'required|numeric'); if ($this->form_validation->run() == FALSE) { $response = array( 'status' => 'error', 'message' => validation_errors() ); } else { $code = $this->input->post('code'); $secret = $this->session->userdata('two_factor_secret'); $result = $this->two_factor_auth_model->verifyCode($secret, $code); if ($result) { $user = $this->user_model->get_by_account($userId); $user->totp_secret = $this->session->userdata('two_factor_secret'); // 執行更新 $user->update(); // 生成恢復碼 $recovery_codes = $this->recovery_code_model->generateSecureRecoveryCode(); $this->recovery_code_model->insert_recovery_code($user->id, $recovery_codes); $response = array( 'status' => 'success', 'message' => '驗證成功!', 'recoveryCodes' => $recovery_codes ); } else { $response = array( 'status' => 'error', 'message' => '驗證失敗!' ); } } echo json_encode($response); } } |
View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
<html> <head> <title>Two Factor Authentication</title> <!-- 引入 Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <style> body, html { height: 100%; } .container { min-height: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; } </style> </head> <body> <div class="container"> <h2>Scan the QR Code with your 2FA app</h2> <img src="<?php echo $qrCodeUrl; ?>" class="mb-3" /> <!-- 增加 margin-bottom 以便於表單分隔 --> <form> <input type="text" name="code" placeholder="Enter your code" class="form-control mb-2" required title="Code must be a number."> <!-- 使用 Bootstrap 的 form-control --> <input type="submit" value="Verify" class="btn btn-primary" id="verifyButton"> <!-- 使用 Bootstrap 的按鈕樣式 --> </form> <p id="result"></p> </div> <script> $(document).ready(function () { $('form').submit(function (event) { event.preventDefault(); // 阻止表單的傳統提交 var code = $('[name="code"]').val(); // 獲取用戶輸入的代碼 $.ajax({ url: '/TwoFactorAuth/verify', // 控制器方法的URL type: 'POST', data: { code: code }, dataType: 'json', // 指定回應類型為 JSON success: function (response) { if (response.status === 'success') { $('#result').html('<div class="alert alert-success">' + response.message + '</div>'); if (response.recoveryCodes) { var recoveryCodeHtml = '<div class="alert alert-info">' + '<h4>Recovery Code:</h4>' + '<p>Please save this code in a secure place:</p>' + '<p><strong>' + response.recoveryCodes + '</strong></p>' + '</div>'; $('#result').html(recoveryCodeHtml); // 更新結果區域 } } else { $('#result').html('<div class="alert alert-danger">' + response.message + '</div>'); } } }); }); }); </script> </body> </html> |