用戶認證
- 功能:用session紀錄登錄驗證狀態
- 前提:用戶表:dajngo自帶的auth_user
- 建立超級用戶:python3 manage.py createsuperuser
- 它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據。
1、auth模塊方法
from django.contrib import auth
一、authenticate()
- 提供了用戶認證功能,即驗證用戶名以及密碼是否正確,通常須要username 、password兩個關鍵字參數。
- 若是認證成功(用戶名和密碼正確有效),便會返回一個 User 對象。
- authenticate()會在該 User 對象上設置一個屬性來標識後端已經認證了該用戶,且該信息在後續的登陸過程當中是須要的。
- 用法:
user = authenticate(username='theuser',password='thepassword')
二、login(HttpRequest, user)
- 該函數接受一個HttpRequest對象,以及一個通過認證的User對象。
- 該函數實現一個用戶登陸的功能。它本質上會在後端爲該用戶生成相關session數據。
- 注意:只要使用login(request, user_obj)以後,request.user就能拿到當前登陸的用戶對象。不然request.user獲得的是一個匿名用戶對象(AnonymousUser Object)。詳細原理請查看 AuthenticationMiddleware 中間件源碼。
- 用法:
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user_obj = authenticate(username=username, password=password) if user_obj: login(request, user_obj) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
三、logout(request)
- 該函數接受一個HttpRequest對象,無返回值。
- 當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。
- 用法:
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
四、is_authenticated()
- 若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。也能夠用裝飾器:@login_required
- 用法:
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
五、login_requierd()
- auth 給咱們提供的一個裝飾器工具,用來快捷的給某個視圖添加登陸校驗。
- 用法:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
- 若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' 並傳遞當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。
- 若是須要自定義登陸的URL,則須要在settings.py文件中經過LOGIN_URL進行修改。
- 示例:
LOGIN_URL = '/login/' # 這裏配置成你項目登陸頁面的路由
六、create_user()
- auth 提供的一個建立新用戶的方法,須要提供必要參數(username、password)等。
- 建立用戶:user = User.objects.create_user(username='',password='',email='')
- 用法:
from django.contrib.auth.models import User user = User.objects.create_user(username='用戶名',password='密碼',email='郵箱',...)
七、create_superuser()
- auth 提供的一個建立新的超級用戶的方法,須要提供必要參數(username、password)等。
- 用法:
from django.contrib.auth.models import User user_obj = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)
八、check_password(raw_password)
- auth 提供的一個檢查密碼是否正確的方法,須要提供當前請求用戶的密碼。
- 密碼正確返回True,不然返回False。
- check_password(passwd):用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True
- 修改密碼:user = User.objects.get(username='') user.set_password(password='') user.save
- 用法:
ok = user_obj.check_password('密碼')
- 或者直接針對當前請求的user對象校驗原密碼是否正確:
ok = request.user.check_password(raw_password='原密碼')
九、set_password(raw_password)
- auth 提供的一個修改密碼的方法,接收 要設置的新密碼 做爲參數。
- 注意:設置完必定要調用用戶對象的save方法!!!
- 用法:
user_obj.set_password('新密碼') user_obj.save()
![](http://static.javashuo.com/static/loading.gif)
2、用戶對象的屬性
- user_obj可以拿到認證所用用戶表的數據屬性,好比username, password等。
- is_staff : 用戶是否擁有網站的管理權限.
- is_active : 是否容許用戶登陸, 設置爲 False,能夠在不刪除用戶的前提下禁止用戶登陸。
3、擴展默認的auth_user表
- 方式1:新建另一張表而後經過一對一和內置的auth_user表關聯
- 方式2:(推薦)經過繼承內置的 AbstractUser 類,來定義一個本身的Model類。
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用戶信息表 """ nid = models.AutoField(primary_key=True) phone = models.CharField(max_length=11, null=True, unique=True) def __str__(self): return self.username
注意: 按上面的方式擴展了內置的auth_user表以後,必定要在settings.py中告訴Django,如今使用新定義的UserInfo表來作用戶認證。寫法以下:javascript
# 引用Django自帶的User表,繼承使用時須要設置 AUTH_USER_MODEL = "app名.UserInfo"
自定義認證系統默認使用的數據表以後,就能夠像使用默認的auth_user表那樣使用UserInfo表了。好比:php
建立普通用戶:css
UserInfo.objects.create_user(username='用戶名', password='密碼')
建立超級用戶:html
UserInfo.objects.create_superuser(username='用戶名', password='密碼')
4、使用
![](http://static.javashuo.com/static/loading.gif)
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") user=auth.authenticate(username=user,password=pwd) #if 驗證成功返回user對象,不然返回None if user: auth.login(request,user) # request.user:當前登陸對象 是一個全局變量,在任何視圖和模板均可以直接使用。 next_url=request.GET.get("next","/index/") return redirect(next_url) return render(request,"login.html") @login_required def index(request): #username=request.user.username #return render(request,"index.html",{"username":username}) return render(request,"index.html") def logout(request): auth.logout(request) return redirect("/login/") def reg(request): if request.method=="POST": user = request.POST.get("user") pwd = request.POST.get("pwd") user=User.objects.create_user(username=user,password=pwd) #User.objects.create_user return redirect("/login/") return render(request,"reg.html")
一、settings.py配置
![](http://static.javashuo.com/static/loading.gif)
AUTH_USER_MODEL="app01.UserInfo" LOGIN_URL="/login/" LANGUAGE_CODE = 'en-us' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False #====== STATIC_URL = '/static/' STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static"), ] # 與用戶上傳相關的配置 MEDIA_ROOT=os.path.join(BASE_DIR,"media") MEDIA_URL="/media/"
二、app01.models.py
![](http://static.javashuo.com/static/loading.gif)
#app01.models.py from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用戶信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="avatars/default.png") create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) def __str__(self): return self.username
三、app01.views.py
![](http://static.javashuo.com/static/loading.gif)
from django.shortcuts import render, HttpResponse, redirect from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.contrib import auth from app01.Myforms import UserForm from app01.models import UserInfo def login(request): """ 登陸視圖函數: get請求響應頁面 post(Ajax)請求響應字典 :param request: :return: """ if request.method == "POST": response = {"user": None, "msg": None} user = request.POST.get("user") pwd = request.POST.get("pwd") rmb =request.POST.get("rmb") if rmb: request.session.set_expiry(60 * 60 * 24 * 30) check_code = request.POST.get("check_code") session_code = request.session.get("check_code") if check_code: if session_code.upper() == check_code.upper(): user = auth.authenticate(username=user, password=pwd) if user: auth.login(request, user) # request.user== 當前登陸對象 response["user"] = user.username else: response["msg"] = "用戶名或者密碼錯誤!" else: response["msg"] = "驗證碼錯誤!" else: response["msg"] = "驗證碼不能爲空!" return JsonResponse(response) return render(request, "login.html") def logout(request): """ 註銷視圖 :param request: :return: """ auth.logout(request) # request.session.flush() return redirect("/login/") def check_code(request): """ 驗證碼: 字體:static/font/Monaco.ttf check_code = request.session.get("check_code") """ from utils.check_code import create_validate_code img_data = create_validate_code(request) return HttpResponse(img_data) def register(request): """ 註冊視圖函數: get請求響應註冊頁面 post(Ajax)請求,校驗字段,響應字典 :param request: :return: """ if request.is_ajax(): form = UserForm(request,request.POST) response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一條用戶紀錄 user = form.cleaned_data.get("user") pwd = form.cleaned_data.get("pwd") email = form.cleaned_data.get("email") UserInfo.objects.create_user(username=user, password=pwd, email=email,) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm(request) return render(request, "register.html", {"form": form}) @login_required def index(request): pass
四、app01.Myforms.py
![](http://static.javashuo.com/static/loading.gif)
from django import forms from django.forms import widgets from blog.models import UserInfo from django.core.exceptions import ValidationError class BaseForm(object): def __init__(self, request, *args, **kwargs): self.request = request super(BaseForm, self).__init__(*args, **kwargs) class UserForm(BaseForm,forms.Form): user=forms.CharField(min_length=4, max_length=32, error_messages={"required":"用戶名不能爲空"}, label="用戶名", widget=widgets.TextInput(attrs={"class":"form-control"},) ) pwd=forms.CharField(min_length=4, max_length=32, error_messages={'required': '密碼不能爲空.'}, label="密碼", widget=widgets.PasswordInput(attrs={"class":"form-control"},) ) re_pwd=forms.CharField(min_length=4, max_length=32, error_messages={'required': '密碼不能爲空.'}, label="確認密碼", widget=widgets.PasswordInput(attrs={"class":"form-control"},) ) email=forms.EmailField(max_length=32, error_messages={'required': '郵箱不能爲空.'}, label="郵箱", widget=widgets.EmailInput(attrs={"class":"form-control"},) ) check_code=forms.CharField( error_messages={'required': '驗證碼不能爲空.'}, label="驗證碼", widget=widgets.TextInput(attrs={'class':'form-control'},) ) def clean_check_code(self): input_code = self.cleaned_data['check_code'] session_code = self.request.session.get('check_code') if input_code.upper() == session_code.upper(): return input_code raise ValidationError(message='驗證碼錯誤', code='invalid') def clean_user(self): val=self.cleaned_data.get("user") user=UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("該用戶已註冊!") def clean(self): pwd=self.cleaned_data.get("pwd") re_pwd=self.cleaned_data.get("re_pwd") if pwd and re_pwd: if pwd==re_pwd: return self.cleaned_data else: # raise ValidationError("兩次密碼不一致!") self.add_error("re_pwd", ValidationError('兩次密碼不一致')) else: return self.cleaned_data
五、自定義驗證碼 utils.check_code.py
字體文件:"static/font/Monaco.ttf"java
![](http://static.javashuo.com/static/loading.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import random from PIL import Image, ImageDraw, ImageFont, ImageFilter from io import BytesIO _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小寫字母,去除可能干擾的i,l,o,z _upper_cases = _letter_cases.upper() # 大寫字母 _numbers = ''.join(map(str, range(3, 10))) # 數字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) # PIL def create_validate_code(request,size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="static/font/Monaco.ttf", length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance=2): """ @todo: 生成驗證碼圖片 @param size: 圖片的大小,格式(寬,高),默認爲(120, 30) @param chars: 容許的字符集合,格式字符串 @param img_type: 圖片保存的格式,默認爲GIF,可選的爲GIF,JPEG,TIFF,PNG @param mode: 圖片模式,默認爲RGB @param bg_color: 背景顏色,默認爲白色 @param fg_color: 前景色,驗證碼字符顏色,默認爲藍色#0000FF @param font_size: 驗證碼字體大小 @param font_type: 驗證碼字體,默認爲 ae_AlArabiya.ttf @param length: 驗證碼字符個數 @param draw_lines: 是否劃干擾線 @param n_lines: 干擾線的條數範圍,格式元組,默認爲(1, 2),只有draw_lines爲True時有效 @param draw_points: 是否畫干擾點 @param point_chance: 干擾點出現的機率,大小範圍[0, 100] @return: [0]: PIL Image實例 @return: [1]: 驗證碼圖片中的字符串 """ width, height = size # 寬高 # 建立圖形 img = Image.new(mode, size, bg_color) draw = ImageDraw.Draw(img) # 建立畫筆 def get_chars(): """生成給定長度的字符串,返回列表格式""" return random.sample(chars, length) def create_lines(): """繪製干擾線""" line_num = random.randint(*n_line) # 干擾線條數 for i in range(line_num): # 起始點 begin = (random.randint(0, size[0]), random.randint(0, size[1])) # 結束點 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): """繪製干擾點""" chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width): for h in range(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): """繪製驗證碼字符""" c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每一個字符先後以空格隔開 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 圖形扭曲參數 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 建立扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 濾鏡,邊界增強(閾值更大) f = BytesIO() request.session['check_code'] = strs img.save(f, 'png') data = f.getvalue() return data
六、註冊頁面templates.register.html
![](http://static.javashuo.com/static/loading.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .error { color: red; } .register { width: 400px; margin-top: 20px; margin-left: auto; margin-right: auto; border: 1px solid #f0f0f0; padding: 10px 30px 50px 30px; -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); } .register h3 { font-size: 25px; text-align: center; font-weight: bold; } </style> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="register"> <h3>用戶註冊</h3> <form id="form" novalidate> {% csrf_token %} <div class="form-group"> <label for="id_user">{{ form.user.label }}</label> {{ form.user }} <span class="pull-right error">{{ form.user.errors.0 }}</span> </div> <div class="form-group"> <label for="id_pwd">{{ form.pwd.label }}</label> {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span> </div> <div class="form-group"> <label for="id_re_pwd">{{ form.re_pwd.label }}</label> {{ form.re_pwd }} <span class="pull-right error">{{ form.re_pwd.errors.0 }}</span> <!--<span class="pull-right error">{{ errors.0 }}</span>--> </div> <div class="form-group"> <label for="id_email">{{ form.email.label }}</label> {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></div> <div class="form-group"> <label for="id_check_code">{{ form.check_code.label }}</label> <div class="row"> <div class="col-xs-7"> {{ form.check_code }}<span class="pull-right error">{{ form.errors.check_code.0 }}</span> </div> <div class="col-xs-5"> <img id="check" src="/check_code/"> </div> </div> </div> <input type="button" class="btn btn-default reg_btn" value="註冊"/> </form> </div> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script> $("#check").click(function () { old_check = $(this).attr('src'); $(this).attr('src', old_check + '?'); }); // 基於Ajax提交數據 $(".reg_btn").click(function () { //console.log($("#form").serializeArray()); var formdata = new FormData(); var request_data = $("#form").serializeArray(); $.each(request_data, function (index, data) { formdata.append(data.name, data.value) }); $.ajax({ url: "", type: "post", contentType: false, processData: false, data: formdata, success: function (data) { //console.log(data); if (data.user) { // 註冊成功 location.href = "/login/" } else { // 註冊失敗 //console.log(data.msg) // 清空錯誤信息 $("span.error").html(""); $(".form-group").removeClass("has-error"); // 顯示提交的錯誤信息! $.each(data.msg, function (field, error_list) { console.log(field, error_list); if (field == "__all__") { $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) } } }) }) </script> </body> </html>
七、登陸頁面templates.login.html
![](http://static.javashuo.com/static/loading.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .error { color: red; } .login { width: 400px; margin-top: 20px; margin-left: auto; margin-right: auto; border: 1px solid #f0f0f0; padding: 10px 30px 50px 30px; -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); } .login h3 { font-size: 25px; text-align: center; font-weight: bold; } </style> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="login"> <h3> 用戶登錄 </h3> <form> {% csrf_token %} <div class="form-group"> <label for="user">用戶名</label> <input type="text" class="form-control" id="user" placeholder="請輸入用戶名"> </div> <div class="form-group"> <label for="pwd">密碼</label> <input type="password" class="form-control" id="pwd" placeholder="請輸入密碼"> </div> <div class="form-group"> <label for="check_code">驗證碼</label> <div class="row"> <div class="col-xs-7"> <input type="text" class="form-control" id="check_code" placeholder="請輸入驗證碼"> </div> <div class="col-xs-5"> <img id="check" src="/check_code/"> </div> </div> </div> <div class="checkbox"> <label> <input type="checkbox" value="1" name="rmb"> 一個月內自動登錄 </label> <div class="pull-right"> <a href="#">忘記密碼?</a> </div> </div> <input type="button" class="btn btn-default login_btn" value="登 陸"/><span class="error"></span> </form> </div> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script> // 刷新驗證碼 $("#check").click(function () { $(this)[0].src += "?" }); // 登陸驗證 $(".login_btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), check_code: $("#check_code").val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, success: function (data) { console.log(data); if (data.user) { if (location.search){ location.href = location.search.slice(6) } else { location.href = "/index/" } } else { $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}); setTimeout(function(){ $(".error").text(""); },1000) } } }) }) </script> </body> </html>
5、geetest驗證碼
- pip3 install social-auth-app-django
一、urls.py
![](http://static.javashuo.com/static/loading.gif)
from django.contrib import admin from django.urls import path,re_path from django.views.static import serve from app01 import views from django.conf import settings from django.urls import include urlpatterns = [ re_path(r'^alogin/$', views.alogin), re_path(r'^pc-geetest/register', views.pcgetcaptcha, name='pcgetcaptcha'), re_path(r'^pc-geetest/ajax_validate', views.pcajax_validate, name='pcajax_validate'), path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), path('check_code/', views.check_code), path('register/', views.register), re_path('^$', views.index), # media配置: re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}), ]
二、utils.geetest.py
![](http://static.javashuo.com/static/loading.gif)
#!coding:utf8 import sys import random import json import requests import time from hashlib import md5 if sys.version_info >= (3,): xrange = range VERSION = "3.0.0" class GeetestLib(object): FN_CHALLENGE = "geetest_challenge" FN_VALIDATE = "geetest_validate" FN_SECCODE = "geetest_seccode" GT_STATUS_SESSION_KEY = "gt_server_status" API_URL = "http://api.geetest.com" REGISTER_HANDLER = "/register.php" VALIDATE_HANDLER = "/validate.php" JSON_FORMAT = False def __init__(self, captcha_id, private_key): self.private_key = private_key self.captcha_id = captcha_id self.sdk_version = VERSION self._response_str = "" def pre_process(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""): """ 驗證初始化預處理. //TO DO arrage the parameter """ status, challenge = self._register(user_id,new_captcha,JSON_FORMAT,client_type,ip_address) self._response_str = self._make_response_format(status, challenge,new_captcha) return status def _register(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""): pri_responce = self._register_challenge(user_id,new_captcha,JSON_FORMAT,client_type,ip_address) if pri_responce: if JSON_FORMAT == 1: response_dic = json.loads(pri_responce) challenge = response_dic["challenge"] else: challenge = pri_responce else: challenge=" " if len(challenge) == 32: challenge = self._md5_encode("".join([challenge, self.private_key])) return 1,challenge else: return 0, self._make_fail_challenge() def get_response_str(self): return self._response_str def _make_fail_challenge(self): rnd1 = random.randint(0, 99) rnd2 = random.randint(0, 99) md5_str1 = self._md5_encode(str(rnd1)) md5_str2 = self._md5_encode(str(rnd2)) challenge = md5_str1 + md5_str2[0:2] return challenge def _make_response_format(self, success=1, challenge=None,new_captcha=1): if not challenge: challenge = self._make_fail_challenge() if new_captcha: string_format = json.dumps( {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":True}) else: string_format = json.dumps( {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":False}) return string_format def _register_challenge(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""): if user_id: register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format( api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address) else: register_url = "{api_url}{handler}?gt={captcha_ID}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format( api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address) try: response = requests.get(register_url, timeout=2) if response.status_code == requests.codes.ok: res_string = response.text else: res_string = "" except: res_string = "" return res_string def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo='',JSON_FORMAT=1): """ 正常模式的二次驗證方式.向geetest server 請求驗證結果. """ if not self._check_para(challenge, validate, seccode): return 0 if not self._check_result(challenge, validate): return 0 validate_url = "{api_url}{handler}".format( api_url=self.API_URL, handler=self.VALIDATE_HANDLER) query = { "seccode": seccode, "sdk": ''.join( ["python_",self.sdk_version]), "user_id": user_id, "data":data, "timestamp":time.time(), "challenge":challenge, "userinfo":userinfo, "captchaid":gt, "json_format":JSON_FORMAT } backinfo = self._post_values(validate_url, query) if JSON_FORMAT == 1: backinfo = json.loads(backinfo) backinfo = backinfo["seccode"] if backinfo == self._md5_encode(seccode): return 1 else: return 0 def _post_values(self, apiserver, data): response = requests.post(apiserver, data) return response.text def _check_result(self, origin, validate): encodeStr = self._md5_encode(self.private_key + "geetest" + origin) if validate == encodeStr: return True else: return False def failback_validate(self, challenge, validate, seccode): """ failback模式的二次驗證方式.在本地對軌跡進行簡單的判斷返回驗證結果. """ if not self._check_para(challenge, validate, seccode): return 0 validate_result = self._failback_check_result( challenge, validate,) return validate_result def _failback_check_result(self,challenge,validate): encodeStr = self._md5_encode(challenge) if validate == encodeStr: return True else: return False def _check_para(self, challenge, validate, seccode): return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip())) def _md5_encode(self, values): if type(values) == str: values = values.encode() m = md5(values) return m.hexdigest()
三、app01.views.py
![](http://static.javashuo.com/static/loading.gif)
from django.shortcuts import render, HttpResponse, redirect from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.contrib import auth from blog.Myforms import UserForm from blog.models import UserInfo from utils.geetest import GeetestLib pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" def pcajax_validate(request): if request.method == "POST": gt = GeetestLib(pc_geetest_id, pc_geetest_key) challenge = request.POST.get(gt.FN_CHALLENGE, '') validate = request.POST.get(gt.FN_VALIDATE, '') seccode = request.POST.get(gt.FN_SECCODE, '') status = request.session[gt.GT_STATUS_SESSION_KEY] user_id = request.session["user_id"] if status: result = gt.success_validate(challenge, validate, seccode, user_id) else: result = gt.failback_validate(challenge, validate, seccode) result = {"status":"success"} if result else {"status":"fail"} #================ user = request.POST.get("user") pwd = request.POST.get("pwd") user = auth.authenticate(username=user, password=pwd) if not user: result['status'] = "fail" result['msg'] = '用戶名或者密碼錯誤' else: auth.login(request, user) # request.user== 當前登陸對象 模板中{{ request.user.username }} rmb = request.POST.get("rmb") if rmb: request.session.set_expiry(60 * 60 * 24 * 30) #=================== return HttpResponse(json.dumps(result)) return HttpResponse("error") def pcgetcaptcha(request): user_id = 'test' gt = GeetestLib(pc_geetest_id, pc_geetest_key) status = gt.pre_process(user_id) request.session[gt.GT_STATUS_SESSION_KEY] = status request.session["user_id"] = user_id response_str = gt.get_response_str() return HttpResponse(response_str) def alogin(request): return render(request,'alogin.html')
四、templates.alogin.html
![](http://static.javashuo.com/static/loading.gif)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> .login { width: 400px; margin-top: 100px; margin-left: auto; margin-right: auto; border: 1px solid #f0f0f0; padding: 10px 30px 50px 30px; -webkit-box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); box-shadow: 5px 10px 10px rgba(0, 0, 0, .05); } .login h3{font-size: 25px; text-align: center;font-weight: bold;} .alert{ padding: 6px; margin-bottom: 0; } /* 如下遮罩層爲demo.用戶可自行設計實現 */ #mask { display: none; position: fixed; text-align: center; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); overflow: auto; } /* 可自行設計實現captcha的位置大小 */ .popup-mobile { position: relative; } #popup-captcha-mobile { position: fixed; display: none; left: 50%; top: 50%; transform: translate(-50%, -50%); -webkit-transform: translate(-50%, -50%); z-index: 9999; } </style> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> </head> <body> <div class="login"> <h3>用戶登錄</h3> <form> {% csrf_token %} <div class="form-group"> <label for="user">用戶名</label> <input type="text" class="form-control" id="user" placeholder="請輸入用戶名"> </div> <div class="form-group"> <label for="pwd">密碼</label> <input type="password" class="form-control" id="pwd" placeholder="請輸入密碼"> </div> <div class="checkbox"> <label> <input type="checkbox" value="1" name="rmb"> 一個月內自動登錄 </label> <div class="pull-right"> <a href="#">忘記密碼?</a> </div> </div> <input id="popup-submit" type="button" class="btn btn-default login_btn" value="登 陸"/><span id="error_msg"></span> </form> <div id="popup-captcha"></div> </div> <!-- 爲使用方便,直接使用jquery.js庫,如您代碼中不須要,能夠去掉 --> <script src="http://code.jquery.com/jquery-1.12.3.min.js"></script> <!-- 引入封裝了failback的接口--initGeetest --> <script src="http://static.geetest.com/static/tools/gt.js"></script> <script> var handlerPopup = function (captchaObj) { // 成功的回調 captchaObj.onSuccess(function () { var validate = captchaObj.getValidate(); $.ajax({ url: "/pc-geetest/ajax_validate", // 進行二次驗證 type: "post", dataType: "json", data: { user: $('#user').val(), pwd: $('#pwd').val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), geetest_challenge: validate.geetest_challenge, geetest_validate: validate.geetest_validate, geetest_seccode: validate.geetest_seccode }, success: function (data) { if (data && (data.status === "success")) { location.href = "/index/" } else { {# console.log(data.msg);#} $("#error_msg").text(data.msg).css({"color": "red", "margin-left": "10px"}); setTimeout(function(){ $("#error_msg").text(""); },4000) } } }); }); $("#popup-submit").click(function () { captchaObj.show(); }); // 將驗證碼加到id爲captcha的元素裏 captchaObj.appendTo("#popup-captcha"); // 更多接口參考:http://www.geetest.com/install/sections/idx-client-sdk.html }; // 驗證開始須要向網站主後臺獲取id,challenge,success(是否啓用failback) $.ajax({ url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加隨機數防止緩存 type: "get", dataType: "json", success: function (data) { // 使用initGeetest接口 // 參數1:配置參數 // 參數2:回調,回調的第一個參數驗證碼對象,以後可使用它作appendTo之類的事件 initGeetest({ gt: data.gt, challenge: data.challenge, product: "popup", // 產品形式,包括:float,embed,popup。注意只對PC版驗證碼有效 offline: !data.success // 表示用戶後臺檢測極驗服務器是否宕機,通常不須要關注 // 更多配置參數請參見:http://www.geetest.com/install/sections/idx-client-sdk.html#config }, handlerPopup); } }); </script> </body> </html>