zoukankan      html  css  js  c++  java
  • 谷歌验证器使用亲测可用

    首先介绍一下谷歌验证器,就相当于给一个条网站登录的页面加了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 就是谷歌验证器了,大家可以自己到应用中心下载。

     

    分享技术,方便你我他。
  • 相关阅读:
    再理解HDFS的存储机制
    C实现头插法和尾插法来构建单链表(不带头结点)
    linux系统编程:线程同步-相互排斥量(mutex)
    基于github for windows&amp;github的团队协作基本操作
    分治法求众数问题 (配图)
    hdu1576 mod 运算的逆元
    Android5.0(lollipop)新特性介绍(一)
    jenkins详解(一)
    手机APP测试点总结
    App测试方法总结
  • 原文地址:https://www.cnblogs.com/leijiangsheng/p/15778550.html
Copyright © 2011-2022 走看看