Django google-authenticator Google令牌

2020-11-09 10:49:48 浏览数 (1)

Google令牌

#0 github

代码语言:javascript复制
https://github.com/Coxhuang/google-authenticator.git

#1 使用操作

  • 调用绑定google-authenticator的接口,生成一个二维码(如何生成先不用管,后面再说)
  • 手机客户端扫描二维码,App生成一个动态的6位验证码
  • 输入验证码,返回True/False

#2 原理

Google令牌分成两部分,一部分是服务端(Google提供的开源代码),另一部分就是客户端(用户在手机/电脑上安装的app或者插件)

  • (服务端)随机生成一个字符串,并将该字符串 用户唯一标示(这里我用的用户唯一标示是邮箱)构造成固定的格式生成一个二维码
  • (客户端)手机下载google-authenticator客户端,扫描二维码,二维码的信息(字符串 用户唯一标示)会保存在客户端内,App通过算法生成一个6位的验证码(验证码会通过时间的变化,30秒更新一次)
  • (服务端)服务端使用Google提供的代码,把App提供的验证码 邮箱进行校验

#3 实例讲解

需求分析

  • 用户登陆时,除了需要用户名和密码,还需要提供该用户对应的Google令牌验证码

使用步骤

新增用户(跳过这一步骤)
绑定google-authenticator

调用绑定令牌接口效果图

拿返回的数据生成二维码,因为是前后端分离的项目,所以生成二维码交给前端处理,我这里使用网上在线生成二维码工具(https://cli.im/text?3dd4ec76bb965fb69effefc6a95b8ff8)

使用手机App扫描二维码

登陆

输入错误的令牌

输入正确的令牌,会生成token,也就是登陆成功

#4 具体代码讲解(本例子是前后端分离项目,只考虑后端,前端代码忽略,后端代码基于Django RestFramework)

#4.1 需求分析

  • 在用户登陆时,除了需要用户提供账号密码,还需要用户提供该用户实时的令牌验证码
  • 登陆成功返回TOKEN

#4.2 绑定令牌

先上代码

代码语言:javascript复制
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)

请求头数据(用户名 密码)

代码语言:javascript复制
{
	"username":"user",
	"password":"cox123456"
}

表结构

代码语言:javascript复制
class Google2Auth(models.Model):
    user = models.OneToOneField(UserProfile,on_delete=models.CASCADE)
    key = models.CharField(verbose_name="Google秘钥",max_length=128)

#4.2 登陆

代码语言:javascript复制
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提供的算法会返回是否匹配

App

  • Google令牌 扫码器(如果手机只安装Google令牌App扫码失败,请安装扫码器) 链接:https://pan.baidu.com/s/1XeO7p4IvNuvzQOiZrq4wtw 提取码:e70f
  • Chrome插件(不需要手机App,用插件就能绑定)
代码语言:javascript复制
https://chrome.google.com/webstore/detail/authenticator/bhghoamapcdpbohphigoooaddinpkbai

0 人点赞