# Python 谷歌令牌



**python 实现google authenticator 认证**

### 一、环境描述

* windows开发环境
* python: 3.6
* 所需安装包: pyotp qrcode Image

### 二、实现原理

* 1.使用pyotp 的python模块生成google auth 需要的密钥
* 2.根据密钥生成条形码图片
* 3.使用google authenticator 客户端扫描条形码，客户端根据时间及密钥经过算法生成6位数的验证码
* 4.平台二次认证通过对输入的验证码进行校验，校验也是基于时间和密钥

### 三、代码实现

#### 3.1随机密钥生成

```python
# a.安装模块包 pyotp
pip install pyotp


# b.密钥生成
import pyotp
gtoken = pyotp.random_base32(64) #获取随机密钥，存于用户表中,随机64位
```

#### 3.2 图片二维码生成

```python
# a. 安装模块包
pip install qrcode

qrcode 依赖 Image 这个包：

pip install Image


# b.根据用户名及密钥生成二维码

import os
import traceback
from qrcode import QRCode, constants


def get_qrcode(secret_key, username):
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    dirpath = os.path.join(BASE_DIR, 'mfa', 'static', 'image')

    data = pyotp.totp.TOTP(secret_key).provisioning_uri(username, issuer_name="IAM MFA Code")
    qr = QRCode(
        version=1,
        error_correction=constants.ERROR_CORRECT_L,
        box_size=6,
        border=4, )
    try:
        qr.add_data(data)
        qr.make(fit=True)
        img = qr.make_image()
        filepath = dirpath + os.sep + secret_key + '.png'
        print('filepath',filepath)
        img.save(filepath)  # 保存条形码图片
        return True,filepath
    except Exception as e:
        traceback.print_exc()
        return False,None



# c. 使用功能
result = get_qrcode(gtoken, 'supery')
print('result', result)
LWGOAZX5PQQLR34BOJRLVHHC4CRVFVXP3PGCYHWZ2PVKLRSJNCDWR6XPL4LAJRST
filepath D:\dev\PYSCRIPTS\xxx\mfa\static\image\LWGOAZX5PQQLR34BOJRLVHHC4CRVFVXP3PGCYHWZ2PVKLRSJNCDWR6XPL4LAJRST.png
result True
```

#### 3.3 效验验证码正确性

```python
# a. 下载好google authenticator客户端


客户扫描图片，前端页面验证用户名和密码后，显示对应的条形码图片

windows上打开图片后扫描二维码
D:\dev\PYSCRIPTS\xxx\mfa\static\image\LWGOAZX5PQQLR34BOJRLVHHC4CRVFVXP3PGCYHWZ2PVKLRSJNCDWR6XPL4LAJRST.png
实际生产环境通过页面直接显示该二维码图片，进行扫描


# b. 效验代码
import pyotp

def Google_Verify_Result(secret_key, verifycode):
    t = pyotp.TOTP(secret_key)

    result = t.verify(verifycode)  # 对输入验证码进行校验，正确返回True
    msg = result if result is True else False
    return msg



# c. 使用功能

secret_key = 'LWGOAZX5PQQLR34BOJRLVHHC4CRVFVXP3PGCYHWZ2PVKLRSJNCDWR6XPL4LAJRST'

res = Google_Verify_Result(secret_key, 247724)
print('res', res)  # res False || res True
```

### 四、完整代码

```python
import os
import traceback
import pyotp
from qrcode import QRCode, constants


class GoogleAuthenticatorClient:
    def __init__(self, secret_key=None):
        self.secret_key = secret_key

    def create_secret(self):
        """
        生成google auth 需要的密钥
        :return:
        """
        self.secret_key = pyotp.random_base32(64)
        return self.secret_key

    def create_secret_qrcode(self, name=None, issuer_name=None, save_to_file=True):
        """
        根据用户名及密钥生成二维码图片
        :param name:用户名
        :param issuer_name:发行人
        :param save_to_file: 保存至文件
        :return:
        """
        data = pyotp.totp.TOTP(self.secret_key).provisioning_uri(name=name, issuer_name=issuer_name)
        qr = QRCode(
            version=1,
            error_correction=constants.ERROR_CORRECT_L,
            box_size=6,
            border=4, )
        try:
            qr.add_data(data)
            qr.make(fit=True)
            img = qr.make_image()
            if save_to_file:
                base_dir = os.path.dirname(os.path.abspath(__file__))
                dir_path = os.path.join(base_dir, 'static', 'image')
                if not os.path.exists(dir_path):
                    os.makedirs(dir_path)
                filepath = dir_path + os.sep + self.secret_key + '.png'
                img.save(filepath)  # 保存条形码图片
                return True, filepath
            else:
                return img.get_image()
        except Exception as e:
            traceback.print_exc()
            return False, None

    def verify_code_func(self, verify_code):
        t = pyotp.TOTP(self.secret_key)
        result = t.verify(verify_code)
        return result


if __name__ == '__main__':
    secret_key = 'PU6PY6FWPVQ4BXE7ZP6X7YMVM3BH3ODS7SW53GL3LJPED7AAQUVF2EKP6AGNFFOX'
    google_auth_ = GoogleAuthenticatorClient(secret_key=secret_key)

    # 随机秘钥生成,使用动态开启口令验证，每次key
    # secret = google_auth_.create_secret()
    # print('秘钥', secret)

    # # 生成图片二维码， save_to_file=True, 图片保存在本地， False 表示不保存，并显示在当前
    image = google_auth_.create_secret_qrcode(name='slp', issuer_name='GoldBull', save_to_file=True)
    print(image.show())

    # 通过谷歌验证码器验证， 返回 True，表示通过， False 表示不通过
    res = google_auth_.verify_code_func(verify_code='138914')
    print(res)

```
