用戶登錄秉氧、登出眷昆、注冊功能的實現(xiàn)

## 用戶注冊功能

[TOC]

### 一、設(shè)計接口思路

-?分析業(yè)務邏輯汁咏,明確在這個業(yè)務中需要涉及到幾個相關(guān)子業(yè)務隙赁,將每個子業(yè)務當做一個接口來設(shè)計

-?分析接口的功能任務,明確接口的訪問方式與返回數(shù)據(jù):

??-?接口的請求方式梆暖,如GET 伞访、POST 、PUT等

??-?接口的URL路徑定義

??-?需要前端傳遞的數(shù)據(jù)及數(shù)據(jù)格式(如路徑參數(shù)轰驳、查詢字符串厚掷、請求體表單弟灼、JSON等)

??-?返回給前端的數(shù)據(jù)及數(shù)據(jù)格式

### 二、功能分析

-?用戶名判斷是否存在

-?手機號判斷是否存在

-?圖片驗證碼

-?短信驗證碼

-?注冊保存用戶數(shù)據(jù)

圖片驗證碼冒黑、短信驗證碼考慮到后續(xù)可能會在其他業(yè)務中也會用到田绑,因此將驗證碼功能獨立出來,**創(chuàng)建一個新應用verifications抡爹,在此應用中實現(xiàn)圖片驗證碼掩驱、短信驗證碼**

### 三、圖片驗證碼接口代碼實現(xiàn)

#### 1.分析

**請求方法**:**GET**

**url定義**:`/image_codes/<uuid:image_code_id>/`

**請求參數(shù)**:url路徑參數(shù)

| 參數(shù)??????????| 類型???????| 前端是否必須傳 | 描述???????????|

| ------------- | ---------- | -------------- | -------------- |

| image_code_id | uuid字符串 | 是?????????????| 圖片驗證碼編號 |

uuid:Universally unique identifier(eg. 123e4567-e89b-12d3-a456-426655440000)

#### 2.后端代碼實現(xiàn)

a.將生成圖像驗證碼的模塊文件夾(百度云盤有提供captcha文件夾)復制粘貼到項目根目錄utils文件夾下

b.由于驗證(圖片驗證冬竟、短信驗證)功能欧穴,以后有可能在其他應用或項目中重用,所以單獨創(chuàng)建一個應用來實現(xiàn)泵殴,所有驗證相關(guān)的業(yè)務邏輯接口涮帘。在apps目錄中創(chuàng)建一個verifications應用,并在settings.py文件中的INSTALLED_APPS列表中指定笑诅。

```python

# 在verifications/views.py文件中添加如下代碼:

import logging

from django.shortcuts import render

from django.views import View

from django_redis import get_redis_connection

from django.http import HttpResponse

from utils.captcha.captcha import captcha

# 安裝圖片驗證碼所需要的 Pillow 模塊

# pip install Pillow

from . import constants

from users.models import Users

# 導入日志器

logger = logging.getLogger('django')

class ImageCode(View):

????"""

????define image verification view

????# /image_codes/<uuid:image_code_id>/

????"""

????def get(self, request, image_code_id):

????????text, image = captcha.generate_captcha()


????????# 確保settings.py文件中有配置redis CACHE

????????# Redis原生指令參考 http://redisdoc.com/index.html

????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables

????????con_redis = get_redis_connection(alias='verify_codes')

????????img_key = "img_{}".format(image_code_id).encode('utf-8')

????????# 將圖片驗證碼的key和驗證碼文本保存到redis中调缨,并設(shè)置過期時間

????????con_redis.setex(img_key, constants.IMAGE_CODE_REDIS_EXPIRES, text)

????????logger.info("Image code: {}".format(text))


????????return HttpResponse(content=image, content_type="images/jpg")

```

c.為了保存應用中用到的常量信息,需要在verifications應用下創(chuàng)建一個constants.py文件

```python

# 在verifications/constants.py文件中加入如下代碼:

# 圖片驗證碼redis有效期吆你,單位秒

IMAGE_CODE_REDIS_EXPIRES = 5 * 60

```

d.本項目需要將圖形驗證碼弦叶、短信驗證碼以及用戶的會話信息保存到redis服務器中,所以需要在settings.py文件中指定如下配置信息:

```python

# settings.py文件中加入如下內(nèi)容:

CACHES = {

????"default": {

????????"BACKEND": "django_redis.cache.RedisCache",??# 指定redis緩存后端

????????"LOCATION": "redis://127.0.0.1:6379/0",

????????"OPTIONS": {

????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",

????????????# "PASSWORD": "mysecret"

????????}

????},

????# 同樣可以指定多個redis

????"session": {

????????"BACKEND": "django_redis.cache.RedisCache",

????????"LOCATION": "redis://127.0.0.1:6379/1",

????????"OPTIONS": {

????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",

????????}

????},

????"verify_codes": {

????????"BACKEND": "django_redis.cache.RedisCache",

????????"LOCATION": "redis://127.0.0.1:6379/2",

????????"OPTIONS": {

????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",

????????}

????},

????"sms_codes": {

????????"BACKEND": "django_redis.cache.RedisCache",

????????"LOCATION": "redis://127.0.0.1:6379/3",

????????"OPTIONS": {

????????????"CLIENT_CLASS": "django_redis.client.DefaultClient",

????????}

????},

}

# 將用戶的session保存到redis中

SESSION_ENGINE = "django.contrib.sessions.backends.cache"

# 指定緩存redis的別名

SESSION_CACHE_ALIAS = "session"

```

e.在verifications應用下創(chuàng)建一個urls.py文件并添加如下內(nèi)容:

```python

# verifications應用下創(chuàng)建一個urls.py

from django.urls import path, re_path

from . import views

app_name = "verifications"

urlpatterns = [

????# re_path(r'^image_codes/(?P<image_code_id>[\w-]+)/$', view=views.ImageCodeView.as_view(), name="image_code"),

????# image_code_id為uuid格式

????path('image_codes/<uuid:image_code_id>/', views.ImageCode.as_view(), name='image_code'),

]

```

#### 3.前端代碼實現(xiàn)

html代碼:

```jinja2

{# 繼承base基類模版 #}

{% extends 'base/base.html' %}

{% block link %}

{#??<link rel="stylesheet" href="../../static/css/users/auth.css">#}

??<link rel="stylesheet" href="{% static 'css/users/auth.css' %}">

{% endblock %}

{% block title %}

注冊

{% endblock %}

<!-- main-contain start??-->

{% block main_start %}

<main id="container">

??<div class="register-contain">

????<div class="top-contain">

??????<h4 class="please-register">請注冊</h4>

??????<a href="javascript:void(0);" class="login">立即登錄 &gt;</a>

????</div>

????<form action="" method="post" class="form-contain">

??????<div class="form-item">

????????<input type="text" placeholder="請輸入用戶名" name="username" class="form-control"??autocomplete="off">

??????</div>

??????<div class="form-item">

????????<input type="password" placeholder="請輸入密碼" name="password" class="form-control">

??????</div>

??????<div class="form-item">

????????<input type="password" placeholder="請輸入確認密碼" name="password_repeat" class="form-control">

??????</div>

??????<div class="form-item">

????????<input type="tel" placeholder="請輸入手機號" name="telephone" class="form-control"??autocomplete="off" autofocus>

??????</div>

??????<div class="form-item">

????????<input type="text" placeholder="請輸入圖形驗證碼" name="captcha_graph" class="form-captcha">

????????<a href="javascript:void(0);" class="captcha-graph-img">

??????????<img src="" alt="驗證碼" title="點擊刷新" >

????????</a>

??????</div>

??????<div class="form-item">

????????<input type="text" placeholder="請輸入短信驗證碼" name="sms_captcha" class="form-captcha" autocomplete="off">

????????<a href="javascript:void(0);" class="sms-captcha" title="發(fā)送驗證碼">獲取短信驗證碼</a>

??????</div>

??????<div class="form-item">

????????<input type="submit" value="立即注冊" class="register-btn">

??????</div>

????</form>

??</div>

</main>

{% endblock %}

<!-- main-contain??end -->

{% block hot_recommend %}

{% endblock %}

{% block script %}

{#??<script src="/static/js/users/auth.js"></script>#}

??<script src="{% static 'js/users/auth.js' %}"></script>

{% endblock %}

```

在js文件夾下創(chuàng)建一個users文件夾用戶存放用戶模塊相關(guān)的js文件妇多,在users文件下創(chuàng)建auth.js文件湾蔓。

```javascript

$(function () {

??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽

??let sImageCodeId = "";??// 定義圖像驗證碼ID值

??generateImageCode();??// 生成圖像驗證碼圖片

??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片

??// 生成一個圖片驗證碼的編號,并設(shè)置頁面中圖片驗證碼img標簽的src屬性

??function generateImageCode() {

????// 1砌梆、生成一個圖片驗證碼隨機編號

????sImageCodeId = generateUUID();

????// 2默责、拼接請求url /image_codes/<uuid:image_code_id>/

????let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";

????// 3、修改驗證碼圖片src地址

????$img.attr('src', imageCodeUrl)

??}

??// 生成圖片UUID驗證碼

??function generateUUID() {

????let d = new Date().getTime();

????if (window.performance && typeof window.performance.now === "function") {

????????d += performance.now(); //use high-precision timer if available

????}

????let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

????????let r = (d + Math.random() * 16) % 16 | 0;

????????d = Math.floor(d / 16);

????????return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);

????});

????return uuid;

??}


});

```

### 四咸包、判斷用戶名是否注冊功能實現(xiàn)

#### 1.分析

**請求方法**:**GET**

**url定義**:`/usernames/(?P<username>\w{5,20})/`

**請求參數(shù)**:url路徑參數(shù)

| 參數(shù)?????| 類型???| 前端是否必須傳 | 描述?????????????|

| -------- | ------ | -------------- | ---------------- |

| username | 字符串 | 是?????????????| 用戶輸入的用戶名 |

#### 2.后端代碼實現(xiàn)

```python

from utils.json_fun import to_json_data

from django.views import View

class CheckUsernameView(View):

????"""

????Check whether the user exists?

????GET usernames/(?P<username>\w{5,20})/

????"""

????def get(self, request, username):

????????# count = 1 if User.objects.get(username=username) else 0

????????data = {

????????????'username': username,

????????????'count': Users.objects.filter(username=username).count()

????????}

????????return to_json_data(data=data)

```

在項目根目錄中的utils目錄下創(chuàng)建json_fun.py文件桃序,用于處理json格式轉(zhuǎn)化功能。

```python

from django.http import JsonResponse

from .res_code import Code

def to_json_data(errno=Code.OK, errmsg='', data=None, kwargs=None):

????json_dict = {'errno': errno, 'errmsg': errmsg, 'data': data}

????if kwargs and isinstance(kwargs, dict) and kwargs.keys():

????????json_dict.update(kwargs)

????return JsonResponse(json_dict)

```

在項目根目錄中的utils目錄下創(chuàng)建res_code.py文件烂瘫,用于把后端執(zhí)行的情況返回給前端媒熊。

```python

class Code:

????OK??????????????????= "0"

????DBERR???????????????= "4001"

????NODATA??????????????= "4002"

????DATAEXIST???????????= "4003"

????DATAERR?????????????= "4004"

????METHERR?????????????= "4005"

????SMSERROR????????????= "4006"

????SMSFAIL?????????????= "4007"

????SESSIONERR??????????= "4101"

????LOGINERR????????????= "4102"

????PARAMERR????????????= "4103"

????USERERR?????????????= "4104"

????ROLEERR?????????????= "4105"

????PWDERR??????????????= "4106"


????SERVERERR???????????= "4500"

????UNKOWNERR???????????= "4501"

error_map = {

????Code.OK????????????????????: "成功",

????Code.DBERR?????????????????: "數(shù)據(jù)庫查詢錯誤",

????Code.NODATA????????????????: "無數(shù)據(jù)",

????Code.DATAEXIST?????????????: "數(shù)據(jù)已存在",

????Code.DATAERR???????????????: "數(shù)據(jù)錯誤",

????Code.METHERR???????????????: "方法錯誤",

????Code.SMSERROR??????????????: "發(fā)送短信驗證碼異常",

????Code.SMSFAIL???????????????: "發(fā)送短信驗證碼失敗",

????Code.SESSIONERR????????????: "用戶未登錄",

????Code.LOGINERR??????????????: "用戶登錄失敗",

????Code.PARAMERR??????????????: "參數(shù)錯誤",

????Code.USERERR???????????????: "用戶不存在或未激活",

????Code.ROLEERR???????????????: "用戶身份錯誤",

????Code.PWDERR????????????????: "密碼錯誤",


????Code.SERVERERR?????????????: "內(nèi)部錯誤",

????Code.UNKOWNERR?????????????: "未知錯誤",

}

```

```python

# url 定義

from django.urls import path, re_path

from . import views

app_name = "verifications"

urlpatterns = [

????# image_code_id為uuid格式

????path('image_codes/<uuid:image_code_id>/', views.ImageCode.as_view(), name='image_code'),

????re_path('usernames/(?P<username>\w{5,20})/', views.CheckUsernameView.as_view(), name='check_username'),

]

```

#### 3.前端代碼實現(xiàn)

```javascript

$(function () {

??let $username = $('#user_name');

??let $img = $(".form-item .captcha-graph-img img");

??let sImageCodeId = "";

??// 1、圖像驗證碼邏輯

??generateImageCode();??// 生成圖像驗證碼圖片

??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片

??// 2坟比、用戶名驗證邏輯

??$username.blur(function () {

????fn_check_usrname();

??});

??// 生成一個圖片驗證碼的編號芦鳍,并設(shè)置頁面中圖片驗證碼img標簽的src屬性

??function generateImageCode() {

????// 1、生成一個圖片驗證碼隨機編號

????sImageCodeId = generateUUID();

????// 2葛账、拼接請求url /image_codes/<uuid:image_code_id>/

????let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";

????// 3柠衅、修改驗證碼圖片src地址

????$img.attr('src', imageCodeUrl)

??}

??// 生成圖片UUID驗證碼

??function generateUUID() {

????let d = new Date().getTime();

????if (window.performance && typeof window.performance.now === "function") {

??????d += performance.now(); //use high-precision timer if available

????}

????let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

??????let r = (d + Math.random() * 16) % 16 | 0;

??????d = Math.floor(d / 16);

??????return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);

????});

????return uuid;

??}

??// 判斷用戶名是否已經(jīng)注冊

??function fn_check_usrname() {

????let sUsername = $username.val();??// 獲取用戶名字符串

????if (sUsername === "") {

??????message.showError('用戶名不能為空!');

??????return

????}

????if (!(/^\w{5,20}$/).test(sUsername)) {

??????message.showError('請輸入5-20個字符的用戶名');

??????return

????}

????// 發(fā)送ajax請求籍琳,去后端查詢用戶名是否存在

????$.ajax({

??????url: '/usernames/' + sUsername + '/',

??????type: 'GET',

??????dataType: 'json',

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.username + '已注冊菲宴,請重新輸入贷祈!')

????????} else {

??????????message.showInfo(res.data.username + '能正常使用!')

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時喝峦,請重試势誊!');

??????});

??}

});

```

### 五、判斷手機號是否注冊功能實現(xiàn)

#### 1.分析

**請求方法**:**GET**

**url定義**:`/mobiles/(?P<mobile>1[3-9]\d{9})/`

**請求參數(shù)**:url路徑參數(shù)

| 參數(shù)???| 類型???| 前端是否必須傳 | 描述?????????????|

| ------ | ------ | -------------- | ---------------- |

| mobile | 字符串 | 是?????????????| 用戶輸入的手機號 |

#### 2.后端代碼實現(xiàn)

```python

# 在verifications目錄下的views.py文件中定義如下類視圖:

class CheckMobileView(View):

????"""

????Check whether the mobile exists

????GET mobiles/(?P<mobile>1[3-9]\d{9})/

????"""

????def get(self, request, mobile):

????????data = {

????????????'mobile': mobile,

????????????'count': Users.objects.filter(mobile=mobile).count()

????????}

????????return to_json_data(data=data)

```

```python

# 在verifications目錄下的urls.py文件中定義如下路由:

from django.urls import path, re_path

from . import views

app_name = "verifications"

urlpatterns = [

????re_path('mobiles/(?P<mobile>1[3-9]\d{9})/', views.CheckMobileView.as_view(), name='check_mobiles'),

]

```

#### 3.前端代碼實現(xiàn)

```python

$(function () {

??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素谣蠢,需要定義一個id為user_name

??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽

??let sImageCodeId = "";??// 定義圖像驗證碼ID值

??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素粟耻,需要定義一個id為mobile

??// 1、圖像驗證碼邏輯

??generateImageCode();??// 生成圖像驗證碼圖片

??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片

??// 判斷用戶是否注冊

??// 2眉踱、用戶名驗證邏輯

??$username.blur(function () {

????fn_check_usrname();

??});

??// 3挤忙、手機號驗證邏輯

??// 判斷用戶手機號是否注冊

??$mobile.blur(function () {

????fn_check_mobile();

??});

??// 生成一個圖片驗證碼的編號,并設(shè)置頁面中圖片驗證碼img標簽的src屬性

??function generateImageCode() {

????// 1勋锤、生成一個圖片驗證碼隨機編號

????sImageCodeId = generateUUID();

????// 2、拼接請求url /image_codes/<uuid:image_code_id>/

????let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";

????// 3侥祭、修改驗證碼圖片src地址

????$img.attr('src', imageCodeUrl)

??}

??// 生成圖片UUID驗證碼

??function generateUUID() {

????let d = new Date().getTime();

????if (window.performance && typeof window.performance.now === "function") {

??????d += performance.now(); //use high-precision timer if available

????}

????let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

??????let r = (d + Math.random() * 16) % 16 | 0;

??????d = Math.floor(d / 16);

??????return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);

????});

????return uuid;

??}

??// 判斷用戶名是否已經(jīng)注冊

??function fn_check_usrname() {

????let sUsername = $username.val();??// 獲取用戶名字符串

????if (sUsername === "") {

??????message.showError('用戶名不能為空叁执!');

??????return

????}

????// test()方法 判斷字符串中是否匹配到正則表達式內(nèi)容,返回的是boolean值 ( true / false )

????if (!(/^\w{5,20}$/).test(sUsername)) {

??????message.showError('請輸入5-20個字符的用戶名');

??????return

????}

????// 發(fā)送ajax請求矮冬,去后端查詢用戶名是否存在

????$.ajax({

??????url: '/usernames/' + sUsername + '/',

??????type: 'GET',

??????dataType: 'json',

??????// data:{'code':300268}

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.username + '已注冊谈宛,請重新輸入!')

????????} else {

??????????message.showInfo(res.data.username + '能正常使用胎署!')

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時吆录,請重試!');

??????});

??}

??function fn_check_mobile() {

????let sMobile = $mobile.val();??// 獲取用戶輸入的手機號碼字符串

????let SreturnValue = "";

????if (sMobile === "") {

??????message.showError('手機號不能為空琼牧!');

??????return

????}

????if (!(/^1[345789]\d{9}$/).test(sMobile)) {

??????message.showError('手機號碼格式不正確恢筝,請重新輸入!');

??????return

????}

????$.ajax({

??????url: '/mobiles/' + sMobile + '/',

??????type: 'GET',

??????dataType: 'json',

??????async: false????// 把async關(guān)掉

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.mobile + '已注冊巨坊,請重新輸入撬槽!')

??????????SreturnValue = ""

????????} else {

??????????SreturnValue = "success"

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時,請重試趾撵!');

????????SreturnValue = ""

??????});

????return SreturnValue

??}


});

```

### 六侄柔、發(fā)送手機短信驗證碼功能實現(xiàn)

#### 1.分析

業(yè)務處理流程:

-?判斷圖片驗證碼是否正確

-?判斷是否在60s內(nèi)有發(fā)送記錄

-?生成短信驗證碼

-?保存短信驗證碼與發(fā)送記錄

-?發(fā)送短信

**請求方法**:**POST**

**url定義**:`/sms_codes/`

**請求參數(shù)**:url路徑參數(shù)

| 參數(shù)??????????| 類型???| 前端是否必須傳 | 描述?????????????????????|

| ------------- | ------ | -------------- | ------------------------ |

| mobile????????| 字符串 | 是?????????????| 用戶輸入的手機號?????????|

| image_code_id | UUID???| 是?????????????| js生成的圖片uuid號???????|

| text??????????| 字符串 | 是?????????????| 用戶輸入的圖片驗證碼文本 |

注:由于是post請求,在向后端發(fā)起請求時占调,需要附帶csrf token

#### 2.后端代碼實現(xiàn)

```python

# 在verifications目錄下的views.py文件中定義如下類視圖:

import logging

import json

import random

import string

from django.views import View

from django_redis import get_redis_connection

from . import constants

from utils.json_fun import to_json_data

from utils.res_code import Code, error_map

from users.models import Users

from . import forms

from utils.yuntongxun.sms import CCP

# 導入日志器

logger = logging.getLogger('django')

class SmsCodesView(View):

????"""

????send mobile sms code

????POST /sms_codes/

????"""

????def post(self, request):

????????# 1暂题、

????????json_data = request.body

????????if not json_data:

????????????return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

????????# 將json轉(zhuǎn)化為dict

????????dict_data = json.loads(json_data.decode('utf8'))

????????# 2、

????????form = forms.CheckImgCodeForm(data=dict_data)

????????if form.is_valid():

????????????# 獲取手機號

????????????mobile = form.cleaned_data.get('mobile')

????????????# 3究珊、

????????????# 創(chuàng)建短信驗證碼內(nèi)容

????????????sms_num = ''.join([random.choice(string.digits) for _ in range(constants.SMS_CODE_NUMS)])

????????????# 將短信驗證碼保存到數(shù)據(jù)庫

????????????# 確保settings.py文件中有配置redis CACHE

????????????# Redis原生指令參考 http://redisdoc.com/index.html

????????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables

????????????# 4薪者、

????????????redis_conn = get_redis_connection(alias='verify_codes')

????????????pl = redis_conn.pipeline()

????????????# 創(chuàng)建一個在60s以內(nèi)是否有發(fā)送短信記錄的標記

????????????sms_flag_fmt = "sms_flag_{}".format(mobile)

????????????# 創(chuàng)建保存短信驗證碼的標記key

????????????sms_text_fmt = "sms_{}".format(mobile)

????????????# 此處設(shè)置為True會出現(xiàn)bug

????????????try:

????????????????pl.setex(sms_flag_fmt.encode('utf8'), constants.SEND_SMS_CODE_INTERVAL, 1)

????????????????pl.setex(sms_text_fmt.encode('utf8'), constants.SMS_CODE_REDIS_EXPIRES, sms_num)

????????????????# 讓管道通知redis執(zhí)行命令

????????????????pl.execute()

????????????except Exception as e:

????????????????logger.debug("redis 執(zhí)行出現(xiàn)異常:{}".format(e))

????????????????return to_json_data(errno=Code.UNKOWNERR, errmsg=error_map[Code.UNKOWNERR])

????????????logger.info("Sms code: {}".format(sms_num))

????????????# 發(fā)送短語驗證碼

????????????try:

????????????????result = CCP().send_template_sms(mobile,

?????????????????????????????????????????????????[sms_num, constants.SMS_CODE_YUNTX_EXPIRES],

?????????????????????????????????????????????????constants.SMS_CODE_TEMP_ID)

????????????except Exception as e:

????????????????logger.error("發(fā)送驗證碼短信[異常][ mobile: %s, message: %s ]" % (mobile, e))

????????????????return to_json_data(errno=Code.SMSERROR, errmsg=error_map[Code.SMSERROR])

????????????else:

????????????????if result == 0:

????????????????????logger.info("發(fā)送驗證碼短信[正常][ mobile: %s sms_code: %s]" % (mobile, sms_num))

????????????????????return to_json_data(errno=Code.OK, errmsg="短信驗證碼發(fā)送成功")

????????????????else:

????????????????????logger.warning("發(fā)送驗證碼短信[失敗][ mobile: %s ]" % mobile)

????????????????????return to_json_data(errno=Code.SMSFAIL, errmsg=error_map[Code.SMSFAIL])

????????else:

????????????# 定義一個錯誤信息列表

????????????err_msg_list = []

????????????for item in form.errors.get_json_data().values():

????????????????err_msg_list.append(item[0].get('message'))

????????????????# print(item[0].get('message'))???# for test

????????????err_msg_str = '/'.join(err_msg_list)??# 拼接錯誤信息為一個字符串

????????????return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)

```

```python

# 在verifications目錄下的forms.py文件中定義如下form表單:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

-------------------------------------------------

??@Time : 2018/12/3 4:18 PM?

??@Auth : Youkou?

??@Site : www.youkou.site

??@File : forms.py

??@IDE??: PyCharm

??@Edit : 2018/12/3

-------------------------------------------------

"""

from django import forms

from django.core.validators import RegexValidator

from django_redis import get_redis_connection

from users.models import Users

# 創(chuàng)建手機號的正則校驗器

mobile_validator = RegexValidator(r"^1[3-9]\d{9}$", "手機號碼格式不正確")

class CheckImgCodeForm(forms.Form):

????"""

????check image code

????"""

????mobile = forms.CharField(max_length=11, min_length=11, validators=[mobile_validator, ],

?????????????????????????????error_messages={"min_length": "手機號長度有誤", "max_length": "手機號長度有誤",

?????????????????????????????????????????????"required": "手機號不能為空"})

????image_code_id = forms.UUIDField(error_messages={"required": "圖片UUID不能為空"})

????text = forms.CharField(max_length=4, min_length=4,

???????????????????????????error_messages={"min_length": "圖片驗證碼長度有誤", "max_length": "圖片驗證碼長度有誤",

???????????????????????????????????????????"required": "圖片驗證碼不能為空"})

????# Cleaning and validating fields that depend on each other

????def clean(self):

????????cleaned_data = super().clean()

????????# 1、

????????image_uuid = cleaned_data.get("image_code_id")

????????image_text = cleaned_data.get("text")

????????mobile_num = cleaned_data.get("mobile")

????????# 2剿涮、

????????if Users.objects.filter(mobile=mobile_num).count():

????????????raise forms.ValidationError("手機號已注冊啸胧,請重新輸入")

????????# 確保settings.py文件中有配置redis CACHE

????????# Redis原生指令參考 http://redisdoc.com/index.html

????????# Redis python客戶端 方法參考 http://redis-py.readthedocs.io/en/latest/#indices-and-tables

????????# 2赶站、

????????con_redis = get_redis_connection(alias='verify_codes')

????????# 創(chuàng)建保存到redis中圖片驗證碼的key

????????img_key = "img_{}".format(image_uuid).encode('utf-8')

????????# 取出圖片驗證碼

????????real_image_code_origin = con_redis.get(img_key)

????????real_image_code = real_image_code_origin.decode('utf-8') if real_image_code_origin else None

????????con_redis.delete(img_key)

????????# 驗證手機號

????????if (not real_image_code) or (image_text != real_image_code):

????????????raise forms.ValidationError("圖片驗證失敗")

????????# 檢查是否在60s內(nèi)有發(fā)送記錄

????????sms_flag_fmt = "sms_flag_{}".format(mobile_num).encode('utf-8')

????????sms_flag = con_redis.get(sms_flag_fmt)

????????if sms_flag:

????????????raise forms.ValidationError("獲取手機短信驗證碼過于頻繁")

```

```python

# 在verifications目錄下的constants.py文件中定義如下常數(shù):

# 圖片驗證碼redis有效期,單位秒

IMAGE_CODE_REDIS_EXPIRES = 5 * 60

# 短信驗證碼有效期纺念,單位秒

SMS_CODE_REDIS_EXPIRES = 5 * 60

# 云通訊短信驗證碼過期時間(發(fā)送短信是顯示為5分鐘)

SMS_CODE_YUNTX_EXPIRES = 5

# 發(fā)送間隔

SEND_SMS_CODE_INTERVAL = 60

# 短信發(fā)送模板

SMS_CODE_TEMP_ID = 1

# 短信驗證碼位數(shù)

SMS_CODE_NUMS = 6

```

#### 3. 短信驗證碼平臺-云通訊

a.**本項目中使用的短信發(fā)送模塊為**[云通訊](<http://www.yuntongxun.com/>)平臺:

-?用戶發(fā)送短信(或語音)驗證碼的第三方平臺

-?[參考文檔地址](<http://www.yuntongxun.com/doc/ready/demo/1_4_1_2.html>)

b.**注冊登錄**

-?免費注冊登錄

-?贈送8元贝椿,用于測試

-?身份認證之后,才能正常使用

c.**獲取開發(fā)者相關(guān)參數(shù)**

![yuntongxun_1](../images/yuntongxun_1.jpg)

```python

_accountSid = '開發(fā)者主賬號中的ACCOUNT SID'

# 說明:主賬號Token陷谱,登陸云通訊網(wǎng)站后烙博,可在控制臺-應用中看到開發(fā)者主賬號AUTH TOKEN

_accountToken = '開發(fā)者主賬號中的AUTH TOKEN'

# 請使用管理控制臺首頁的APPID或自己創(chuàng)建應用的APPID

_appId = '開發(fā)者主賬號中的AppID(默認)'

# 說明:請求地址,生產(chǎn)環(huán)境配置成app.cloopen.com

_serverIP = 'sandboxapp.cloopen.com'

# 說明:請求端口 烟逊,生產(chǎn)環(huán)境為8883

_serverPort = "8883"

# 說明:REST API版本號保持不變

_softVersion = '2013-12-26'

```

![modify_parm](../images/modify_parms.jpg)

d.**設(shè)置測試賬號**

![edit_test_mobile](../images/yuntongxun_2.jpg)

#### 4.前端代碼實現(xiàn)

```javascript

$(function () {

??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素渣窜,需要定義一個id為user_name

??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽

??let sImageCodeId = "";??// 定義圖像驗證碼ID值

??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素,需要定義一個id為mobile

??let $smsCodeBtn = $('.form-item .sms-captcha');??// 獲取短信驗證碼按鈕元素宪躯,需要定義一個id為input_smscode

??let $imgCodeText = $('#input_captcha');??// 獲取用戶輸入的圖片驗證碼元素乔宿,需要定義一個id為input_captcha

??// 1、圖像驗證碼邏輯

??generateImageCode();??// 生成圖像驗證碼圖片

??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片

??// 2访雪、判斷用戶名是否注冊

??$username.blur(function () {

????fn_check_usrname();

??});

??// 3详瑞、判斷用戶手機號是否注冊

??$mobile.blur(function () {

????fn_check_mobile();

??});

??// 4、發(fā)送短信邏輯

??$smsCodeBtn.click(function () {

????// 判斷手機號是否輸入

????if (fn_check_mobile() !== "success") {

??????return

????}

????// 判斷用戶是否輸入圖片驗證碼

????let text = $imgCodeText.val();??// 獲取用戶輸入的圖片驗證碼文本

????if (!text) {

????????message.showError('請?zhí)顚戲炞C碼臣缀!');

????????return

????}

????// 判斷是否生成的UUID

????if (!sImageCodeId) {

??????message.showError('圖片UUID為空');

??????return

????}

????// 正常獲取參數(shù)

????let SdataParams = {

??????"mobile": $mobile.val(),???// 獲取用戶輸入的手機號

??????"text": text,??// 獲取用戶輸入的圖片驗證碼文本

??????"image_code_id": sImageCodeId??// 獲取圖片UUID

????};

????// for test

????// let SdataParams = {

????//???"mobile": "1886608",???// 獲取用戶輸入的手機號

????//???"text": "ha3d",??// 獲取用戶輸入的圖片驗證碼文本

????//???"image_code_id": "680a5a66-d9e5-4c3c-b8ea"??// 獲取圖片UUID

????// };

????// 向后端發(fā)送請求

????$.ajax({

??????// 請求地址

??????url: "/sms_codes/",

??????// 請求方式

??????type: "POST",

??????data: JSON.stringify(SdataParams),

??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)

??????contentType: "application/json; charset=utf-8",

??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)

??????dataType: "json",

??????async: false????// 關(guān)掉異步功能

????})

??????.done(function (res) {

????????if (res.errno === "0") {

??????????// 倒計時60秒坝橡,60秒后允許用戶再次點擊發(fā)送短信驗證碼的按鈕

???????????message.showSuccess('短信驗證碼發(fā)送成功');

??????????let num = 60;

??????????// 設(shè)置一個計時器

??????????let t = setInterval(function () {

????????????if (num === 1) {

??????????????// 如果計時器到最后, 清除計時器對象

??????????????clearInterval(t);

??????????????// 將點擊獲取驗證碼的按鈕展示的文本恢復成原始文本

??????????????$smsCodeBtn.html("獲取驗證碼");

????????????} else {

??????????????num -= 1;

??????????????// 展示倒計時信息

??????????????$smsCodeBtn.html(num + "秒");

????????????}

??????????}, 1000);

????????} else {

??????????message.showError(res.errmsg);

????????}

??????})

??????.fail(function(){

????????message.showError('服務器超時,請重試精置!');

??????});

??});

??// 生成一個圖片驗證碼的編號计寇,并設(shè)置頁面中圖片驗證碼img標簽的src屬性

??function generateImageCode() {

????// 1、生成一個圖片驗證碼隨機編號

????sImageCodeId = generateUUID();

????// 2脂倦、拼接請求url /image_codes/<uuid:image_code_id>/

????let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";

????// 3番宁、修改驗證碼圖片src地址

????$img.attr('src', imageCodeUrl)

??}

??// 生成圖片UUID驗證碼

??function generateUUID() {

????let d = new Date().getTime();

????if (window.performance && typeof window.performance.now === "function") {

??????d += performance.now(); //use high-precision timer if available

????}

????let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

??????let r = (d + Math.random() * 16) % 16 | 0;

??????d = Math.floor(d / 16);

??????return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);

????});

????return uuid;

??}

??// 判斷用戶名是否已經(jīng)注冊

??function fn_check_usrname() {

????let sUsername = $username.val();??// 獲取用戶名字符串

????let sReturnValue = "";


????if (sUsername === "") {

??????message.showError('用戶名不能為空!');

??????return

????}

????// test()方法 判斷字符串中是否匹配到正則表達式內(nèi)容赖阻,返回的是boolean值 ( true / false )

????if (!(/^\w{5,20}$/).test(sUsername)) {

??????message.showError('請輸入5-20個字符的用戶名');

??????return

????}

????// 發(fā)送ajax請求贝淤,去后端查詢用戶名是否存在

????$.ajax({

??????url: '/usernames/' + sUsername + '/',

??????type: 'GET',

??????dataType: 'json',

??????async: false

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.username + '已注冊,請重新輸入政供!');

??????????sReturnValue = ""

????????} else {

??????????message.showInfo(res.data.username + '能正常使用播聪!');

??????????sReturnValue = ""

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時,請重試布隔!');

????????sReturnValue = ""

??????});


??????return sReturnValue

??}

??function fn_check_mobile() {

????let sMobile = $mobile.val();??// 獲取用戶輸入的手機號碼字符串

????let sReturnValue = "";

????if (sMobile === "") {

??????message.showError('手機號不能為空离陶!');

??????return

????}

????if (!(/^1[345789]\d{9}$/).test(sMobile)) {

??????message.showError('手機號碼格式不正確,請重新輸入衅檀!');

??????return

????}

????$.ajax({

??????url: '/mobiles/' + sMobile + '/',

??????type: 'GET',

??????dataType: 'json',

??????async: false

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.mobile + '已注冊招刨,請重新輸入!')

??????????sReturnValue = ""

????????} else {

??????????SreturnValue = "success"

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時哀军,請重試沉眶!');

????????sReturnValue = ""

??????});

????return sReturnValue

??}

??// get cookie using jQuery

??function getCookie(name) {

????let cookieValue = null;

????if (document.cookie && document.cookie !== '') {

??????let cookies = document.cookie.split(';');

??????for (let i = 0; i < cookies.length; i++) {

????????let cookie = jQuery.trim(cookies[i]);

????????// Does this cookie string begin with the name we want?

????????if (cookie.substring(0, name.length + 1) === (name + '=')) {

??????????cookieValue = decodeURIComponent(cookie.substring(name.length + 1));

??????????break;

????????}

??????}

????}

????return cookieValue;

??}

??function csrfSafeMethod(method) {

????// these HTTP methods do not require CSRF protection

????return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));

??}

??// Setting the token on the AJAX request

??$.ajaxSetup({

????beforeSend: function (xhr, settings) {

??????if (!csrfSafeMethod(settings.type) && !this.crossDomain) {

????????xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

??????}

????}

??});

});

```

### 七打却、用戶注冊功能實現(xiàn)

#### 1.分析

業(yè)務處理流程:

-?判斷用戶名是否為空,是否已注冊

-?判斷手機號是否為空谎倔,是否已注冊

-?判斷密碼是否為空柳击,格式是否正確

-?判斷確認密碼與密碼是否相同

-?判斷短信驗證碼是否為空,是否格式正確片习,是否與真實的短信驗證碼相同

**請求方法**:**POST**

**url定義**:`/users/register/`

**請求參數(shù)**:url路徑參數(shù)

| 參數(shù)????????????| 類型???| 前端是否必須傳 | 描述?????????????????|

| --------------- | ------ | -------------- | -------------------- |

| username????????| 字符串 | 是?????????????| 用戶輸入的用戶名?????|

| password????????| 字符串 | 是?????????????| 用戶輸入的密碼???????|

| password_repeat | 字符串 | 是?????????????| 用戶輸入的重復密碼???|

| mobile??????????| 字符串 | 是?????????????| 用戶輸入的手機號?????|

| sms_code????????| 字符串 | 是?????????????| 用戶輸入的短信驗證碼 |

注:由于是post請求捌肴,在向后端發(fā)起請求時,需要附帶csrf token

#### 2.后端代碼實現(xiàn)

```python

# 在users目錄下的views.py文件中定義如下類視圖:

import json

from django.shortcuts import render

from django.views import View

from .forms import RegisterForm

from .models import Users

from utils.json_fun import to_json_data

from utils.res_code import Code, error_map

class RegisterView(View):

????"""

????"""

????def get(self, request):

????????"""

????????"""

????????return render(request, 'users/register.html')

????def post(self, request):

????????"""

????????"""

????????# 1藕咏、

????????json_data = request.body

????????if not json_data:

????????????return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

????????# 將json轉(zhuǎn)化為dict

????????dict_data = json.loads(json_data.decode('utf8'))

????????form = RegisterForm(data=dict_data)

????????if form.is_valid():

????????????username = form.cleaned_data.get('username')

????????????password = form.cleaned_data.get('password')

????????????mobile = form.cleaned_data.get('mobile')

????????????user = Users.objects.create_user(username=username, password=password, mobile=mobile)

????????????login(request, user)

????????????return to_json_data(errmsg="恭喜您状知,注冊成功!")

????????else:

????????????# 定義一個錯誤信息列表

????????????err_msg_list = []

????????????for item in form.errors.get_json_data().values():

????????????????err_msg_list.append(item[0].get('message'))

????????????err_msg_str = '/'.join(err_msg_list)

????????????return to_json_data(errno=Code.PARAMERR, errmsg=err_msg_str)

```

```python

# 在users目錄下的forms.py文件中定義如下form表單:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

-------------------------------------------------

??@Time : 2018/12/8 2:22 PM?

??@Auth : Youkou?

??@Site : www.youkou.site

??@File : forms.py

??@IDE??: PyCharm

??@Edit : 2018/12/8

-------------------------------------------------

"""

import re

from django import forms

from django_redis import get_redis_connection

from verifications.constants import SMS_CODE_NUMS

from .models import Users

class RegisterForm(forms.Form):

????"""

????"""

????username = forms.CharField(label='用戶名', max_length=20, min_length=5,

???????????????????????????????error_messages={"min_length": "用戶名長度要大于5", "max_length": "用戶名長度要小于20",

???????????????????????????????????????????????"required": "用戶名不能為空"}

???????????????????????????????)

????password = forms.CharField(label='密碼', max_length=20, min_length=6,

???????????????????????????????error_messages={"min_length": "密碼長度要大于6", "max_length": "密碼長度要小于20",

???????????????????????????????????????????????"required": "密碼不能為空"}

???????????????????????????????)

????password_repeat = forms.CharField(label='確認密碼', max_length=20, min_length=6,

??????????????????????????????????????error_messages={"min_length": "密碼長度要大于6", "max_length": "密碼長度要小于20",

??????????????????????????????????????????????????????"required": "密碼不能為空"}

??????????????????????????????????????)

????mobile = forms.CharField(label='手機號', max_length=11, min_length=11,

?????????????????????????????error_messages={"min_length": "手機號長度有誤", "max_length": "手機號長度有誤",

?????????????????????????????????????????????"required": "手機號不能為空"})

????sms_code = forms.CharField(label='短信驗證碼', max_length=SMS_CODE_NUMS, min_length=SMS_CODE_NUMS,

???????????????????????????????error_messages={"min_length": "短信驗證碼長度有誤", "max_length": "短信驗證碼長度有誤",

???????????????????????????????????????????????"required": "短信驗證碼不能為空"})

????def clean_mobile(self):

????????"""

????????"""

????????tel = self.cleaned_data.get('mobile')

????????if not re.match(r"^1[3-9]\d{9}$", tel):

????????????raise forms.ValidationError("手機號碼格式不正確")

????????if Users.objects.filter(mobile=tel).exists():

????????????raise forms.ValidationError("手機號已注冊孽查,請重新輸入饥悴!")

????????return tel

????def clean(self):

????????"""

????????"""

????????#

????????cleaned_data = super().clean()

????????passwd = cleaned_data.get('password')

????????passwd_repeat = cleaned_data.get('password_repeat')

????????if passwd != passwd_repeat:

????????????#

????????????raise forms.ValidationError("兩次密碼不一致")

????????#

????????tel = cleaned_data.get('mobile')

????????sms_text = cleaned_data.get('sms_code')

????????# 建立redis連接

????????redis_conn = get_redis_connection(alias='verify_codes')

????????#

????????sms_fmt = "sms_{}".format(tel).encode('utf-8')

????????#

????????real_sms = redis_conn.get(sms_fmt)

????????#

????????if (not real_sms) or (sms_text != real_sms.decode('utf-8')):

????????????raise forms.ValidationError("短信驗證碼錯誤")

```

```python

# 在users目錄下的urls.py文件中添如下路由:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

-------------------------------------------------

??@Time : 2018/11/29 9:58 AM?

??@Auth : Youkou?

??@Site : www.youkou.site

??@File : urls.py

??@IDE??: PyCharm

??@Edit : 2018/11/29

-------------------------------------------------

"""

from django.urls import path

from . import views

app_name = 'users'

urlpatterns = [

????# path('register/', views.register, name='register'),

????path('register/', views.RegisterView.as_view(), name='register'),

]

```

#### 3.前端代碼實現(xiàn)

```javascript

$(function () {

??let $username = $('#user_name');??// 選擇id為user_name的網(wǎng)頁元素,需要定義一個id為user_name

??let $img = $(".form-item .captcha-graph-img img");??// 獲取圖像標簽

??let sImageCodeId = "";??// 定義圖像驗證碼ID值

??let $mobile = $('#mobile');??// 選擇id為mobile的網(wǎng)頁元素盲再,需要定義一個id為mobile

??let $smsCodeBtn = $('.form-item .sms-captcha');??// 獲取短信驗證碼按鈕元素西设,需要定義一個id為input_smscode

??let $imgCodeText = $('#input_captcha');??// 獲取用戶輸入的圖片驗證碼元素,需要定義一個id為input_captcha

??let $register = $('.form-contain');??// 獲取注冊表單元素


??// 1洲胖、圖片驗證碼邏輯

??// 通過uuid生成驗證碼編號

??// 拼接驗證碼地址

??// 設(shè)置驗證碼圖片標簽的src

??generateImageCode();??// 生成圖像驗證碼圖片

??$img.click(generateImageCode);??// 點擊圖片驗證碼生成新的圖片驗證碼圖片


??// 2济榨、用戶名驗證邏輯

??$username.blur(function () {

????fn_check_usrname();

??});

??// 3坯沪、手機號驗證邏輯

??// 判斷用戶手機號是否注冊

??$mobile.blur(function () {

????fn_check_mobile();

??});


??// 4绿映、發(fā)送短信驗證碼邏輯

??$smsCodeBtn.click(function () {

????// 判斷手機號是否輸入

????if (fn_check_mobile() !== "success") {

??????return

????}

????// 判斷用戶是否輸入圖片驗證碼

????let text = $imgCodeText.val();??// 獲取用戶輸入的圖片驗證碼文本

????if (!text) {

????????message.showError('請?zhí)顚戲炞C碼!');

????????return

????}

????if (!sImageCodeId) {

??????message.showError('圖片UUID為空');

??????return

????}

????// 正常

????let SdataParams = {

??????"mobile": $mobile.val(),???// 獲取用戶輸入的手機號

??????"text": text,???// 獲取用戶輸入的圖片驗證碼文本

??????"image_code_id": sImageCodeId??// 獲取圖片UUID

????};

????// for test

????// let SdataParams = {

????//???"mobile": "1806508",???// 獲取用戶輸入的手機號

????//???"text": "ha3d",??// 獲取用戶輸入的圖片驗證碼文本

????//???"image_code_id": "680a5a66-d9e5-4c3c-b8ea"??// 獲取圖片UUID

????// };

????// 向后端發(fā)送請求

????$.ajax({

??????// 請求地址

??????url: "/sms_codes/",

??????// 請求方式

??????type: "POST",

??????// 向后端發(fā)送csrf token

??????// headers: {

??????//???????????// 根據(jù)后端開啟的CSRFProtect保護腐晾,cookie字段名固定為X-CSRFToken

??????//???????????"X-CSRFToken": getCookie("csrf_token")

??????// },

??????// data: JSON.stringify(SdataParams),

??????data: JSON.stringify(SdataParams),

??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)

??????contentType: "application/json; charset=utf-8",

??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)

??????dataType: "json",

??????async: false

????})

??????.done(function (res) {

????????if (res.errno === "0") {

??????????// 倒計時60秒叉弦,60秒后允許用戶再次點擊發(fā)送短信驗證碼的按鈕

???????????message.showSuccess('短信驗證碼發(fā)送成功');

??????????let num = 60;

??????????// 設(shè)置一個計時器

??????????let t = setInterval(function () {

????????????if (num === 1) {

??????????????// 如果計時器到最后, 清除計時器對象

??????????????clearInterval(t);

??????????????// 將點擊獲取驗證碼的按鈕展示的文本恢復成原始文本

??????????????$smsCodeBtn.html("獲取驗證碼");

????????????} else {

??????????????num -= 1;

??????????????// 展示倒計時信息

??????????????$smsCodeBtn.html(num + "秒");

????????????}

??????????}, 1000);

????????} else {

??????????message.showError(res.errmsg);

????????}

??????})

??????.fail(function(){

????????message.showError('服務器超時,請重試藻糖!');

??????});

??});

??// 5淹冰、注冊邏輯

??$register.submit(function (e) {

????// 阻止默認提交操作

????e.preventDefault();

????// 獲取用戶輸入的內(nèi)容

????let sUsername = $username.val();??// 獲取用戶輸入的用戶名字符串

????let sPassword = $("input[name=password]").val();

????let sPasswordRepeat = $("input[name=password_repeat]").val();

????let sMobile = $mobile.val();??// 獲取用戶輸入的手機號碼字符串

????let sSmsCode = $("input[name=sms_captcha]").val();

????// 判斷用戶名是否已注冊

????if (fn_check_usrname() !== "success") {

??????return

????}

????// 判斷手機號是否為空,是否已注冊

????if (fn_check_mobile() !== "success") {

??????return

????}

????// 判斷用戶輸入的密碼是否為空

????if ((!sPassword) || (!sPasswordRepeat)) {

??????message.showError('密碼或確認密碼不能為空');

??????return

????}

????// 判斷用戶輸入的密碼和確認密碼長度是否為6-20位

????if ((sPassword.length < 6 || sPassword.length > 20) ||

??????(sPasswordRepeat.length < 6 || sPasswordRepeat.length > 20)) {

??????message.showError('密碼和確認密碼的長度需在6~20位以內(nèi)');

??????return

????}

????// 判斷用戶輸入的密碼和確認密碼是否一致

????if (sPassword !== sPasswordRepeat) {

??????message.showError('密碼和確認密碼不一致');

??????return

????}

????// 判斷用戶輸入的短信驗證碼是否為6位數(shù)字

????if (!(/^\d{6}$/).test(sSmsCode)) {

??????message.showError('短信驗證碼格式不正確巨柒,必須為6位數(shù)字樱拴!');

??????return

????}

????// 發(fā)起注冊請求

????// 1、創(chuàng)建請求參數(shù)

????let SdataParams = {

??????"username": sUsername,

??????"password": sPassword,

??????"password_repeat": sPasswordRepeat,

??????"mobile": sMobile,

??????"sms_code": sSmsCode

????};

????// 2洋满、創(chuàng)建ajax請求

????$.ajax({

??????// 請求地址

??????url: "/users/register/",??// url尾部需要添加/

??????// 請求方式

??????type: "POST",

??????data: JSON.stringify(SdataParams),

??????// 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)

??????contentType: "application/json; charset=utf-8",

??????// 響應數(shù)據(jù)的格式(后端返回給前端的格式)

??????dataType: "json",

????})

??????.done(function (res) {

????????if (res.errno === "0") {

??????????// 注冊成功

??????????message.showSuccess('恭喜你晶乔,注冊成功!');

??????????setTimeout(function () {

????????????// 注冊成功之后重定向到主頁

????????????window.location.href = document.referrer;

??????????}, 1000)

????????} else {

??????????// 注冊失敗牺勾,打印錯誤信息

??????????message.showError(res.errmsg);

????????}

??????})

??????.fail(function(){

????????message.showError('服務器超時正罢,請重試!');

??????});

??});


??// 生成一個圖片驗證碼的編號驻民,并設(shè)置頁面中圖片驗證碼img標簽的src屬性

??function generateImageCode() {

????// 1翻具、生成一個圖片驗證碼隨機編號

????sImageCodeId = generateUUID();

????// 2履怯、拼接請求url /image_codes/<uuid:image_code_id>/

????let imageCodeUrl = "/image_codes/" + sImageCodeId + "/";

????// 3、修改驗證碼圖片src地址

????$img.attr('src', imageCodeUrl)

??}

??// 生成圖片UUID驗證碼

??function generateUUID() {

????let d = new Date().getTime();

????if (window.performance && typeof window.performance.now === "function") {

??????d += performance.now(); //use high-precision timer if available

????}

????let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

??????let r = (d + Math.random() * 16) % 16 | 0;

??????d = Math.floor(d / 16);

??????return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);

????});

????return uuid;

??}

??// 判斷用戶名是否已經(jīng)注冊

??function fn_check_usrname() {

????let sUsername = $username.val();??// 獲取用戶名字符串

????let sReturnValue = "";

????if (sUsername === "") {

??????message.showError('用戶名不能為空裆泳!');

??????return

????}


????if (!(/^\w{5,20}$/).test(sUsername)) {

??????message.showError('請輸入5-20個字符的用戶名');

??????return

????}

????// 發(fā)送ajax請求叹洲,去后端查詢用戶名是否存在

????$.ajax({

??????url: '/usernames/' + sUsername + '/',

??????type: 'GET',

??????dataType: 'json',

??????async: false

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.username + '已注冊,請重新輸入晾虑!')

??????????sReturnValue = ""

????????} else {

??????????message.showInfo(res.data.username + '能正常使用疹味!')

??????????sReturnValue = "success"

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時,請重試帜篇!');

????????sReturnValue = ""

??????});

????return sReturnValue

??}


??// 判斷手機號是否注冊

??function fn_check_mobile() {

????let sMobile = $mobile.val();??// 獲取用戶輸入的手機號碼字符串

????let sReturnValue = "";

????if (sMobile === "") {

??????message.showError('手機號不能為空糙捺!');

??????return

????}

????if (!(/^1[345789]\d{9}$/).test(sMobile)) {

??????message.showError('手機號碼格式不正確,請重新輸入笙隙!');

??????return

????}

????$.ajax({

??????url: '/mobiles/' + sMobile + '/',

??????type: 'GET',

??????dataType: 'json',

??????async: false

????})

??????.done(function (res) {

????????if (res.data.count !== 0) {

??????????message.showError(res.data.mobile + '已注冊洪灯,請重新輸入!')

??????????sReturnValue = ""

????????} else {

??????????message.showSuccess(res.data.mobile + '能正常使用竟痰!');

??????????sReturnValue = "success"

????????}

??????})

??????.fail(function () {

????????message.showError('服務器超時签钩,請重試!');

????????sReturnValue = ""

??????});

????return sReturnValue

??}

??// get cookie using jQuery

??function getCookie(name) {

????let cookieValue = null;

????if (document.cookie && document.cookie !== '') {

??????let cookies = document.cookie.split(';');

??????for (let i = 0; i < cookies.length; i++) {

????????let cookie = jQuery.trim(cookies[i]);

????????// Does this cookie string begin with the name we want?

????????if (cookie.substring(0, name.length + 1) === (name + '=')) {

??????????cookieValue = decodeURIComponent(cookie.substring(name.length + 1));

??????????break;

????????}

??????}

????}

????return cookieValue;

??}

??function csrfSafeMethod(method) {

????// these HTTP methods do not require CSRF protection

????return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));

??}

??// Setting the token on the AJAX request

??$.ajaxSetup({

????beforeSend: function (xhr, settings) {

??????if (!csrfSafeMethod(settings.type) && !this.crossDomain) {

????????xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));

??????}

????}

??});

});

```

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坏快,一起剝皮案震驚了整個濱河市铅檩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌莽鸿,老刑警劉巖昧旨,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異祥得,居然都是意外死亡兔沃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門级及,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乒疏,“玉大人,你說我怎么就攤上這事饮焦∨挛猓” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵县踢,是天一觀的道長转绷。 經(jīng)常有香客問我,道長殿雪,這世上最難降的妖魔是什么暇咆? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上爸业,老公的妹妹穿的比我還像新娘其骄。我一直安慰自己,他們只是感情好扯旷,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布拯爽。 她就那樣靜靜地躺著,像睡著了一般钧忽。 火紅的嫁衣襯著肌膚如雪毯炮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天耸黑,我揣著相機與錄音桃煎,去河邊找鬼。 笑死大刊,一個胖子當著我的面吹牛为迈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缺菌,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼葫辐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伴郁?” 一聲冷哼從身側(cè)響起耿战,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焊傅,沒想到半個月后剂陡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡租冠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年鹏倘,在試婚紗的時候發(fā)現(xiàn)自己被綠了薯嗤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顽爹。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骆姐,靈堂內(nèi)的尸體忽然破棺而出镜粤,到底是詐尸還是另有隱情,我是刑警寧澤玻褪,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布肉渴,位于F島的核電站,受9級特大地震影響带射,放射性物質(zhì)發(fā)生泄漏同规。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望券勺。 院中可真熱鬧绪钥,春花似錦、人聲如沸关炼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儒拂。三九已至寸潦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間社痛,已是汗流浹背见转。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒜哀,地道東北人池户。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像凡怎,于是被迫代替她去往敵國和親校焦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些閱讀 2,027評論 0 2
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,738評論 0 38
  • 英文文檔统倒,一開始我也是抗拒的寨典,邊翻譯邊看,也就花費了1個小時基本就閱讀過了房匆,我的英文基礎(chǔ)其實很差耸成。附上鏈接:鏈接:...
    lonecolonel閱讀 9,855評論 3 1
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,292評論 0 10
  • Art上應用啟動快,運行快浴鸿,但是耗費更多存儲空間井氢,安裝時間長,總的來說ART的功效就是"空間換時間"岳链。 ART: ...
    AlexanderPhaf閱讀 628評論 0 0