谷歌身份验证器(Google Authenticator)的使用详情

燕寒
2023-12-01

谷歌身份验证器(Google Authenticator)

前言

Google Authenticator,是谷歌推出的一款动态口令工具,解决大家的google账户遭到恶意攻击的问题;许多安全性比较高的网站都会采用这种工具来验证登录或者交易;这个动态口令就是Google身份验证器每隔30s会动态生成一个6位数的数字。它的作用是:对你的账号进行“二步验证”保护,或者说做一个双重身份验证,来达到提升安全级别的目的。

步骤

第一步:
1. 在您的手机上安装双重验证程序: Google Authenticator
2. iPhone手机: 在App Store中搜索 Google Authenticator
3. android手机:在应用市场中搜索“谷歌身份验证器”,或搜索Google Authenticator
第二步:安装完成后,您需要对该应用程序进行如下配置:第二步:安装完成后,您需要对该应用程序进行如下配置:
在“google Authenticator(身份验证器)”应用程序中,点击“添加新账户(iOS下是+号)”,然后选择“扫描条形码”。
第三步:配置完成后,手机上会显示一个6位数字,每隔30秒变化一次。这个数字即为您的双重验证密码。请勿删除此双重验证密码账户,否则会导致您无法进行账户操作。您可将密钥记录下来:如果误删,可通过手动输入密钥来恢复。输入双重验证码,以开启或关闭双重验证功能。

谷歌身份验证生成秘钥的接口

import base64
import hashlib
import hmac
import time
import datetime
import random as _random
	
def byte_secret(secret):
    missing_padding = len(secret) % 8
if missing_padding != 0:
    secret += '=' * (8 - missing_padding)
return base64.b32decode(secret, casefold=True)

def int_to_bytestring(i, padding=8):
    result = bytearray()
    while i != 0:
        result.append(i & 0xFF)
        i >>= 8
    return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))

#根据约定的密钥计算当前动态密码
def generate_otp(secret):
    for_time = datetime.datetime.now()
    i = time.mktime(for_time.timetuple())
    input = int(i / 30)
    digest = hashlib.sha1
    digits = 6
    if input < 0:
    raise ValueError('input must be positive integer')
    hasher = hmac.new(byte_secret(secret), int_to_bytestring(input), digest)
    hmac_hash = bytearray(hasher.digest())
    offset = hmac_hash[-1] & 0xf
    code = ((hmac_hash[offset] & 0x7f) << 24 |
            (hmac_hash[offset + 1] & 0xff) << 16 |
            (hmac_hash[offset + 2] & 0xff) << 8 |
            (hmac_hash[offset + 3] & 0xff))
    str_code = str(code % 10 ** digits)
    while len(str_code) < digits:
        str_code = '0' + str_code
    return str_code
    
#随机生成一个base32密钥
def random_base32(length=16, random=_random.SystemRandom(),
                  chars=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')):
    return ''.join(
        random.choice(chars)
        for _ in range(length)
    )
    
# 生成二维码地址
def getQRCodeGoogleUrl(name, secret):
    return "otpauth://totp/" + name + "?secret=%s" % secret


if __name__ == '__main__':
    print(type(random_base32()))
    print(generate_otp('XPFBV5JAC3FS42FE'))

功能实现(自己随便写的,仅供参考)

class GenerateOfferView(APIView):
"""双重验证提供参数"""
permission_classes = [IsAuthenticated]

def get(self, request):
    try:
        user = User.objects.get(id=request.user.id)
    except User.DoesNotExist:
        user = None

    if not user:
        return Response({"msg": "请先登录!", "status": status.HTTP_401_UNAUTHORIZED})
    if user.ga == "":
        secret = random_base32()    # 生成秘钥
        redis_conn = get_redis_connection('verify_codes')
        pl = redis_conn.pipeline()
        pl.setex("secret_%s" % user.moble, constants.GOOGLE_SECRET_EXPIRES, secret)
        pl.execute()
        code_url = getQRCodeGoogleUrl(user.username, secret)
        data = {
            "code_url": code_url,
            "secret": secret
        }
        return Response({
            "data": data,
            'status': status.HTTP_200_OK,
            'msg': '请先开启双重验证!'
        })
    else:
        ga_list = user.ga.split("|")
        secret = ga_list[0]
        code_url = getQRCodeGoogleUrl(user.username, secret)
        data = {
            "code_url": code_url,
            'ga_code': secret,
            "go_login": ga_list[1],
            "go_transfer": ga_list[2]
        }
        return Response({
            "data": data,
            "status": status.HTTP_200_OK,
        })
		
class GenerateView(GenericAPIView):
"""双重验证"""
permission_classes = [IsAuthenticated]
serializer_class = GenerateSerializer

def post(self, request):
    ser = self.get_serializer(data=request.data)
    ser.is_valid(raise_exception=True)
    ga_code = ser.validated_data['ga_code']
    ga_login = ser.validated_data['ga_login']
    ga_transfer = ser.validated_data['ga_transfer']
    mold = ser.validated_data['mold']

    try:
        user = User.objects.get(id=request.user.id)
    except User.DoesNotExist:
        user = None
    if not user:
        return Response({"msg": "请先登录", 'status': status.HTTP_401_UNAUTHORIZED})
    if not ga_code:
        return Response({"msg": "请输入验证码!", "status": status.HTTP_400_BAD_REQUEST})

    if int(mold) == 0:
        redis_conn = get_redis_connection('verify_codes')
        secret = redis_conn.get('secret_%s' % user.moble)

        if not secret:
            return Response({'msg': "验证码已经失效,请刷新网页", "status": status.HTTP_400_BAD_REQUEST})
        if ga_code != generate_otp(secret):
            return Response({"msg": "验证码输入有误!", "status": status.HTTP_400_BAD_REQUEST})

        user.ga = secret.decode() + "|" + ga_login + "|" + ga_transfer
        user.save()
        code_url = getQRCodeGoogleUrl(user.username, secret)
        return Response({"data": {"qrCodeUrl": code_url, "secret": secret}, "status": status.HTTP_200_OK})

    elif int(mold) == 1 or int(mold) == 2:
        if not user.ga:
            return Response({'msg': "还未设置谷歌验证码", "status": status.HTTP_400_BAD_REQUEST})
        if not ga_code:
            return Response({"msg": "请输入验证码!", "status": status.HTTP_400_BAD_REQUEST})
        ga_list = user.ga.split("|")
        secret = ga_list[0]
        if ga_code == generate_otp(str(secret)):
            user.ga = ""
            user.save()
            secret = random_base32()
            redis_conn = get_redis_connection('verify_codes')
            pl = redis_conn.pipeline()
            pl.setex("secret_%s" % user.moble, constants.GOOGLE_SECRET_EXPIRES, secret)
            pl.execute()
            code_url = getQRCodeGoogleUrl(user.username, secret)
            data = {
                "code_url": code_url,
                "secret": secret
            }
            return Response({
                "data": data,
                'status': status.HTTP_200_OK,
                'msg': '解除绑定成功!'
            })
        else:
            return Response({"msg": "验证未通过,请重新验证!", "status": status.HTTP_400_BAD_REQUEST})

使用思路

1、第一次请求,未绑定谷歌验证码,生成随机base32的秘钥,二维码,供绑定使用;
2、绑定验证,将手机app生成的验证码输入验证,成功后将秘钥存入数据库;
3、绑定过后每次请求查询数据库的秘钥生成二维码传出;
4、取消绑定,清除数据库的数据。

 类似资料: