Django組件-用戶認證

用戶認證

  • 功能:用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()
修改密碼示例

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、使用

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")
View Code

一、settings.py配置

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/"
View Code

二、app01.models.py

#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
View Code

三、app01.views.py

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
View Code

四、app01.Myforms.py

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
View Code

五、自定義驗證碼 utils.check_code.py

字體文件:"static/font/Monaco.ttf"java

#!/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
View Code

六、註冊頁面templates.register.html

<!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>
View Code

七、登陸頁面templates.login.html

<!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>
View Code

5、geetest驗證碼

  • pip3 install social-auth-app-django

一、urls.py

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}),
]
View Code

二、utils.geetest.py

#!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()
View Code

三、app01.views.py

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')
View Code

四、templates.alogin.html

<!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>
View Code

五、app01.models.py和app01.Myforms.py同上

相關文章
相關標籤/搜索