https://github.com/Coxhuang/google-authenticator.git
Google令牌分成两部分,一部分是服务端(Google提供的开源代码),另一部分就是客户端(用户在手机/电脑上安装的app或者插件)
拿返回的数据生成二维码,因为是前后端分离的项目,所以生成二维码交给前端处理,我这里使用网上在线生成二维码工具(https://cli.im/text?3dd4ec76bb965fb69effefc6a95b8ff8)
使用手机App扫描二维码
登陆
输入错误的令牌
先上代码
class googleSerializer(DynamicFieldsMixin,serializers.ModelSerializer):
username = serializers.CharField()
password = serializers.CharField()
class Meta:
model = models.UserProfile
fields = ["username","password",]
def validate_username(self, username):
user = authenticate(username=username, password=self.initial_data["password"]) # 验证需要绑定令牌的用户账号密码是否[匹配
if not user:
raise Http404("账号密码不匹配")
return username
class googleBindAPI(APIView):
def post(self,request):
queryset = models.Google2Auth.objects.filter(Q(user__username=request.data["username"]) | Q(user__email=request.data["username"]))
if queryset.exists():
return Response({"success": False, "msg": "已经绑定令牌,绑定失败", "results": None},status=status.HTTP_400_BAD_REQUEST)
serializer = googleSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = models.UserProfile.objects.get(Q(email=request.data["username"]) | Q(username=request.data["username"]))
login(request, user)
base_32_secret = base64.b32encode(codecs.decode(codecs.encode('{0:020x}'.format(random.getrandbits(80))), 'hex_codec'))
totp_obj = googletotp.TOTP(base_32_secret.decode("utf-8"))
qr_code = re.sub(r'=+$', '', totp_obj.provisioning_uri(request.user.email))
models.Google2Auth.objects.create(user=user)
key = str(base_32_secret,encoding="utf-8")
queryset.update(key=key)
return Response({"success": True, "msg": "绑定成功","results": {"将此数据生成二维码": qr_code}}, status=status.HTTP_201_CREATED)
请求头数据(用户名+密码)
{
"username":"user",
"password":"cox123456"
}
表结构
class Google2Auth(models.Model):
user = models.OneToOneField(UserProfile,on_delete=models.CASCADE)
key = models.CharField(verbose_name="Google秘钥",max_length=128)
class loginView(APIView):
def post(self,request):
user = authenticate(username=request.data["username"], password=request.data["password"])
if not user:
raise Http404("账号密码不匹配")
try:
# 判断用户是否已经绑定Google令牌
key = models.Google2Auth.objects.get(
Q(user__username=request.data["username"])|Q(user__email=request.data["username"])).key
except:
raise Http404("未绑定令牌")
if not Google_Verify_Result(key, request.data["code"]):
# 验证令牌
return Response({"success": True, "msg": "令牌失效", "results": None}, status=status.HTTP_400_BAD_REQUEST)
login(request, user)
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({ "success": True, "msg": "登录成功","results": token},status=status.HTTP_200_OK)
随机生成的字符串在客户端保存是通过二维码保存,在服务端保存在数据库中,用户在App上拿到的验证码是App中的算法经过随机字符串+时间戳+其他 生成的(这里的随机字符串和时间戳可以理解为盐),然后用户在登录时,经过服务端的算法时,把用户对应的字符串+验证码+本地时间戳,Google提供的算法会返回是否匹配
Google令牌+扫码器(如果手机只安装Google令牌App扫码失败,请安装扫码器)
链接:https://pan.baidu.com/s/1XeO7p4IvNuvzQOiZrq4wtw
提取码:e70f
Chrome插件(不需要手机App,用插件就能绑定)
https://chrome.google.com/webstore/detail/authenticator/bhghoamapcdpbohphigoooaddinpkbai