天天看點

【Django】使用谷歌身份驗證Google authenticator

1.說明

為了安全,可以使用Google authenticator作為二次驗證的條件,使用起來很簡單,隻需要使用Google authenticator APP掃描一下二維碼即可得到六位數驗證碼,驗證碼每30秒變一次

2.代碼

模型類GoogleAuth

class GoogleAuth(models.Model):
    user_id = models.IntegerField(null=False, verbose_name="使用者ID")
    user_name = models.CharField(null=False, max_length=255, verbose_name="使用者名")
    token = models.CharField(null=False, max_length=255, verbose_name="秘鑰")
    ...
    class Meta:
        db_table = "google_auth"
        verbose_name = "谷歌驗證"
        verbose_name_plural = verbose_name
           

生成二維碼和驗證

import pyotp
import traceback
from qrcode import QRCode, constants
# 生成二維碼
def get_google_qrcode(token, username):
    data = pyotp.totp.TOTP(token).provisioning_uri(username, issuer_name="wallet_webconsole")
    qr = QRCode(error_correction=constants.ERROR_CORRECT_L, version=1, box_size=6, border=4)
    try:
        qr.add_data(data)
        qr.make(fit=True)
        img = qr.make_image()
        # filepath = dirpath + os.sep + secret_key + '.png'
        # img.save(filepath)  # 儲存條形碼圖檔
        # return filepath
        return img
    except Exception as e:
        traceback.print_exc()
        return None
# 判斷驗證碼是否正确
def verify_google_code(token, code):
    t = pyotp.TOTP(token)
    result = t.verify(code)
    return result if result is True else False
           

視圖函數

def Login(request):
    if request.method != "POST":
        return render(request, 'webconsole/login.html')
    username = request.POST.get("username")
    password = request.POST.get("password")
    auth_code = request.POST.get("auth_code")
    auth_code = int(auth_code) if auth_code and auth_code.isdigit() else None
    user = auth.authenticate(username=username, password=password)
    res_data = {
        "code": 0
    }
    if not user:
        res_data.update({"error": "賬号或密碼錯誤"})
        return JsonResponse(res_data)
    google_auth = GoogleAuth.objects.filter(user_id=user.id).first()
    if not google_auth:
        res_data.update({"error": "還未綁定谷歌身份驗證"})
        return JsonResponse(res_data)
    if auth_code:
        if not verify_google_code(google_auth.token, auth_code):
            res_data.update({"code": 2})
            res_data.update({"error": "請輸入正确的谷歌驗證碼"})
            return JsonResponse(res_data)
        auth.login(request, user)
        res_data.update({"code": 1})
        res_data.update({"result": "登入成功"})
        return JsonResponse(res_data)

    qrcode = get_google_qrcode(google_auth.token, user.username)
    if not qrcode:
        res_data.update({"error": "生成谷歌驗證碼失敗"})
        return JsonResponse(res_data)
    # buf = BytesIO()
    # qrcode.save(buf)
    # image_stream = buf.getvalue()
	# return HttpResponse(image_stream, content_type="image/png")  # 傳回二進制流
    out = BytesIO()
    qrcode.save(out, 'PNG')
    pic_base64 = base64.b64encode(out.getvalue()).decode('ascii')  # 傳回base64
    res_data.update({"code": 3})
    res_data.update({"base64": pic_base64})
    return JsonResponse(res_data)
           

上面的demo是為了友善直接把很多代碼都放在一起了,實際生産環境建議還是把視圖函數的業務分開寫