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是為了友善直接把很多代碼都放在一起了,實際生産環境建議還是把視圖函數的業務分開寫