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、取消绑定,清除数据库的数据。