谷歌验证器使用亲测可用

首先介绍一下谷歌验证器,就相当于给一个条网站登录的页面加了2把锁,第一把是自己的账号密码,第二把是谷歌验证器生成的数字code(875 265)

接下来如何使用,调用GoogleAuthenticator.php 类,下面看一个简单的demo

<?php 
session_start();
require_once './GoogleAuthenticator.php';
$ga = new PHPGangsta_GoogleAuthenticator();
if(!empty($_POST['user'])&&!empty($_POST['pass'])&&!empty($_POST['Verification'])){
	//"安全密匙SecretKey" 入库,和账户关系绑定,客户端也是绑定这同一个"安全密匙SecretKey"
    $secret = $_SESSION['login_secret'];
	 //第一个参数是"标识",第二个参数为"安全密匙SecretKey" 生成二维码信息
	$oneCode = $_POST['Verification'];
	$checkResult = $ga->verifyCode($secret, $oneCode, 2);
	if($checkResult){
		$user = $_POST['user'];
		$pass = $_POST['pass'];
		if($user=='admin'&&$pass=='123456'){
			exit('登录成功');
		}else{
			exit('密码错误');
		}
	}else{
		exit('验证码错误');
	}
}else{
    $secret = $ga->createSecret();
    $_SESSION["login_secret"] = $secret;
    $qrCodeUrl = $ga->getQRCodeGoogleUrl('https://bb.fswkh.cn/', $secret);
}
?>
<!DOCTYPE html>
<html>
<head>
	<title>登录</title>
</head>
<body>
	<div>
		<form action="login.php" method="post">
			<p>账号:<input type="text" name="user" placeholder="账号"></p>
			<p>密码:<input type="pass" name="pass"></p>
			<p>验证码:<input type="number" name="Verification"></p>
			<input type="submit" value="提交">
		</form>
		<h2>验证码</h2>
		<img src="<?php  echo $qrCodeUrl; ?> ">
	</div>
</body>
</html>

  

  1 <?php
  2 
  3 /**
  4  * PHP Class for handling Google Authenticator 2-factor authentication.
  5  *
  6  * @author Michael Kliewe
  7  * @copyright 2012 Michael Kliewe
  8  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  9  *
 10  * @link http://www.phpgangsta.de/
 11  */
 12 class PHPGangsta_GoogleAuthenticator
 13 {
 14     protected $_codeLength = 6;
 15 
 16     /**
 17      * Create new secret.
 18      * 16 characters, randomly chosen from the allowed base32 characters.
 19      *
 20      * @param int $secretLength
 21      *
 22      * @return string
 23      */
 24     public function createSecret($secretLength = 16)
 25     {
 26         $validChars = $this->_getBase32LookupTable();
 27 
 28         // Valid secret lengths are 80 to 640 bits
 29         if ($secretLength < 16 || $secretLength > 128) {
 30             throw new Exception('Bad secret length');
 31         }
 32         $secret = '';
 33         $rnd = false;
 34         if (function_exists('random_bytes')) {
 35             $rnd = random_bytes($secretLength);
 36         } elseif (function_exists('mcrypt_create_iv')) {
 37             $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
 38         } elseif (function_exists('openssl_random_pseudo_bytes')) {
 39             $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
 40             if (!$cryptoStrong) {
 41                 $rnd = false;
 42             }
 43         }
 44         if ($rnd !== false) {
 45             for ($i = 0; $i < $secretLength; ++$i) {
 46                 $secret .= $validChars[ord($rnd[$i]) & 31];
 47             }
 48         } else {
 49             throw new Exception('No source of secure random');
 50         }
 51         var_dump($secret);
 52         return $secret;
 53     }
 54 
 55     /**
 56      * Calculate the code, with given secret and point in time.
 57      *
 58      * @param string   $secret
 59      * @param int|null $timeSlice
 60      *
 61      * @return string
 62      */
 63     public function getCode($secret, $timeSlice = null)
 64     {
 65         if ($timeSlice === null) {
 66             $timeSlice = floor(time() / 30);
 67         }
 68 
 69         $secretkey = $this->_base32Decode($secret);
 70 
 71         // Pack time into binary string
 72         $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
 73         // Hash it with users secret key
 74         $hm = hash_hmac('SHA1', $time, $secretkey, true);
 75         // Use last nipple of result as index/offset
 76         $offset = ord(substr($hm, -1)) & 0x0F;
 77         // grab 4 bytes of the result
 78         $hashpart = substr($hm, $offset, 4);
 79 
 80         // Unpak binary value
 81         $value = unpack('N', $hashpart);
 82         $value = $value[1];
 83         // Only 32 bits
 84         $value = $value & 0x7FFFFFFF;
 85 
 86         $modulo = pow(10, $this->_codeLength);
 87 
 88         return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
 89     }
 90 
 91     /**
 92      * Get QR-Code URL for image, from google charts.
 93      *
 94      * @param string $name
 95      * @param string $secret
 96      * @param string $title
 97      * @param array  $params
 98      *
 99      * @return string
100      */
101     public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
102     {
103         var_dump($secret);
104         $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
105         $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
106         $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';
107 
108         $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
109         if (isset($title)) {
110             $urlencoded .= urlencode('&issuer='.urlencode($title));
111         }
112 
113         return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.'';
114     }
115 
116     /**
117      * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now.
118      *
119      * @param string   $secret
120      * @param string   $code
121      * @param int      $discrepancy      This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
122      * @param int|null $currentTimeSlice time slice if we want use other that time()
123      *
124      * @return bool
125      */
126     public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
127     {
128         if ($currentTimeSlice === null) {
129             $currentTimeSlice = floor(time() / 30);
130         }
131 
132         if (strlen($code) != 6) {
133             return false;
134         }
135 
136         for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
137             $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
138             if ($this->timingSafeEquals($calculatedCode, $code)) {
139                 return true;
140             }
141         }
142 
143         return false;
144     }
145 
146     /**
147      * Set the code length, should be >=6.
148      *
149      * @param int $length
150      *
151      * @return PHPGangsta_GoogleAuthenticator
152      */
153     public function setCodeLength($length)
154     {
155         $this->_codeLength = $length;
156 
157         return $this;
158     }
159 
160     /**
161      * Helper class to decode base32.
162      *
163      * @param $secret
164      *
165      * @return bool|string
166      */
167     protected function _base32Decode($secret)
168     {
169         if (empty($secret)) {
170             return '';
171         }
172 
173         $base32chars = $this->_getBase32LookupTable();
174         $base32charsFlipped = array_flip($base32chars);
175 
176         $paddingCharCount = substr_count($secret, $base32chars[32]);
177         $allowedValues = array(6, 4, 3, 1, 0);
178         if (!in_array($paddingCharCount, $allowedValues)) {
179             return false;
180         }
181         for ($i = 0; $i < 4; ++$i) {
182             if ($paddingCharCount == $allowedValues[$i] &&
183                 substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) {
184                 return false;
185             }
186         }
187         $secret = str_replace('=', '', $secret);
188         $secret = str_split($secret);
189         $binaryString = '';
190         for ($i = 0; $i < count($secret); $i = $i + 8) {
191             $x = '';
192             if (!in_array($secret[$i], $base32chars)) {
193                 return false;
194             }
195             for ($j = 0; $j < 8; ++$j) {
196                 $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
197             }
198             $eightBits = str_split($x, 8);
199             for ($z = 0; $z < count($eightBits); ++$z) {
200                 $binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : '';
201             }
202         }
203 
204         return $binaryString;
205     }
206 
207     /**
208      * Get array with all 32 characters for decoding from/encoding to base32.
209      *
210      * @return array
211      */
212     protected function _getBase32LookupTable()
213     {
214         return array(
215             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //  7
216             'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
217             'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
218             'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
219             '=',  // padding char
220         );
221     }
222 
223     /**
224      * A timing safe equals comparison
225      * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html.
226      *
227      * @param string $safeString The internal (safe) value to be checked
228      * @param string $userString The user submitted (unsafe) value
229      *
230      * @return bool True if the two strings are identical
231      */
232     private function timingSafeEquals($safeString, $userString)
233     {
234         if (function_exists('hash_equals')) {
235             return hash_equals($safeString, $userString);
236         }
237         $safeLen = strlen($safeString);
238         $userLen = strlen($userString);
239 
240         if ($userLen != $safeLen) {
241             return false;
242         }
243 
244         $result = 0;
245 
246         for ($i = 0; $i < $userLen; ++$i) {
247             $result |= (ord($safeString[$i]) ^ ord($userString[$i]));
248         }
249 
250         // They are only identical strings if $result is exactly 0...
251         return $result === 0;
252     }
253 }
View Code

 看看最后的效果截图,最后扫码的这个APP 就是谷歌验证器了,大家可以自己到应用中心下载。

 

分享技术,方便你我他。
原文地址:https://www.cnblogs.com/leijiangsheng/p/15778550.html