pip install django-google-auth2
这3个文件复制到当前appa目录下,我们的业务方法会用到它里面的逻辑。
注入django-google-auth2包到项目
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp1',
'rest_framework',
'rest_framework.authtoken',
'rest_framework_jwt',
'django_google_auth2',
]
google动态认证用要数据库,所以要在models.py作一些处理,它用到两个类:
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserProfile(AbstractUser):
#用户信息表,直接继承AbstractUser,根据需要自己修改;
pass
用于存储秘钥的表,根用户表通过id关联
class Google2Auth(models.Model):
"""GoogleAuth"""
user = models.OneToOneField(UserProfile, on_delete=models.CASCADE)
key = models.CharField(verbose_name="Google秘钥", max_length=128)
这里包括新建用户,绑定用户和认证用户三个类,实现我们的业务逻辑功能。
from drf_dynamic_fields import DynamicFieldsMixin
from rest_framework.views import APIView
from . import models
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
import base64
import codecs
import random
import re
import pyotp
from . import googletotp
from django.shortcuts import Http404
from django.db.models import Q
from rest_framework import status
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from django.contrib.auth import authenticate, login
class UserSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
class Meta:
model = models.UserProfile
fields = ["username", "password", "email", ]
def create(self, validated_data):
user = models.UserProfile.objects.create_user(**validated_data) # 新增用户必须用create_user,否则密码不是秘文
return user
# 新建用户
class createUser(mixins.CreateModelMixin, GenericViewSet):
queryset = models.UserProfile.objects.all()
serializer_class = UserSerializer
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
# 绑定Google令牌
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():
raise Http404("已经绑定令牌,绑定失败")
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": qr_code}},
status=status.HTTP_201_CREATED)
def Google_Verify_Result(secret_key, verifycode):
t = pyotp.TOTP(secret_key)
result = t.verify(verifycode) # 对输入验证码进行校验,正确返回True
res = result if result is True else False
print("ret:", res)
return res
# 用户认证
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)
修改urls.py把url和我们的方法联系起来
app_name = 'myapp1'
from django.conf.urls import include
from django.urls import path
from django_google_auth2.google.bindgoogleauth.bindgoogleauthapi import bind_google_auth_api
from rest_framework import routers
from . import views, gauth
createUserViewRouter = routers.DefaultRouter() # 新增用户
createUserViewRouter.register('', gauth.createUser,)
urlpatterns = [
path('createuser/', include(createUserViewRouter.urls)), # 新增用户
path('binggoogleauth/', gauth.googleBindAPI.as_view()), # 绑定令牌
path('loginx/', gauth.loginView.as_view()), #
path('bing-google-auth-api/', bind_google_auth_api),
path('testapi/', views.testapi), # 测试基础API
path('testlogin/', views.testlogin.as_view()), # 测试带参数的api
]
{
"success": true,
"msg": "绑定成功",
"results": {
"qr_code": "otpauth://totp/[email protected]?secret=VRDIV5XHMVVU637N"
}
}
把这段代码生成二维码或者手动输入到google验证APP中去,就可以实时生成动态密码了:
在浏览器输入:http://127.0.0.1:8000/myapp1/loginx/参数为:帐号、密码和动态密码
结果返回登陆成功,同时返回最新有效的token。
失败返回出错信息