1、前臺用戶模型
前臺用戶模型定義
創建前臺模型文件 apps/front/models.py
# -*- encoding: utf-8 -*-
"""
@File : models.py
@Time : 2020/5/11 10:00
@Author : chen
前臺模型文件 apps/front/models.py
"""
# 前臺管理的模型
from exts import db # 數據庫連接
import shortuuid # 前臺用戶id加密
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash # 導入密碼加密,解密方法的庫
import enum # 導入枚舉
# 性別選擇的類
class GenderEnum(enum.Enum):
MALE = 1
FEMALE = 2
SECRET = 3
UNKNOW = 4
# 前臺用戶模型類
class Front_User(db.Model):
__tablename__ = "front_user"
# id 類型不用db.Integer類型,使用String是爲了防止爆破,同時使用shortuuid進行加密
id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)
telephone = db.Column(db.String(11), nullable=False, unique=True) # 非空唯一
username = db.Column(db.String(150), nullable=False)
_password = db.Column(db.String(150), nullable=False) # 密碼加密操作修改字段
email = db.Column(db.String(50), unique=True)
realname = db.Column(db.String(50))
avatar = db.Column(db.String(150)) # 頭像,二進制數據
signatrue = db.Column(db.String(500)) # 簽名
gender = db.Column(db.Enum(GenderEnum), default=GenderEnum.UNKNOW) # 性別枚舉類,默認未知
join_time = db.Column(db.DateTime, default=datetime.now) # 默認當前時間
# 修改密碼加密操作,manage.py映射數據庫時候,使用字段保持相同,由於字段太多,使用傳參形式
def __init__(self, *args, **kwargs):
if 'password' in kwargs: # 如果傳參中包含有
self.password = kwargs.get('password') # 獲取該參數值賦值給password
kwargs.pop('password') # 模型參數中是_password,不是password,彈出
# super(FrontUser, self).__init__(*args, **kwargs) # python2的寫法
super().__init__(*args, **kwargs)
# 密碼加密操作
@property
def password(self): # 密碼取值
return self._password
@password.setter # 密碼加密
def password(self, raw_password):
self._password = generate_password_hash(raw_password)
# 用於驗證前臺登錄密碼是否和數據庫一致,raw_password是前臺登錄輸入的密碼
def check_password(self, raw_password):
result = check_password_hash(self.password, raw_password) # 相當於用相同的hash加密算法加密raw_password,檢測與數據庫中是否一致
return result
前臺用戶模型映射到數據庫
映射數據庫信息文件manage.py
# -*- encoding: utf-8 -*-
"""
@File : manage.py
@Time : 2020/5/10 17:36
@Author : chen
"""
from flask_script import Manager
from bbs import app # 需要將當前文件夾設置爲當前根目錄,纔不會報錯
from flask_migrate import Migrate, MigrateCommand
from exts import db
# 導入後臺模型 才能映射到數據庫 導入後端的模型
from apps.cms.models import CMS_User
# 導入後臺角色模型,映射到數據庫 CMSPersmission角色權限定義類
from apps.cms.models import CMSRole, CMSPersmission
# 導入前臺模型 才能映射到數據庫
from apps.front.models import Front_User
manage = Manager(app)
Migrate(app, db)
manage.add_command('db', MigrateCommand)
# 命令行添加後臺用戶
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
@manage.option('-e', '--email', dest='email')
def create_cms_user(username, password, email):
user = CMS_User(username=username, password=password, email=email)
# 添加映射到數據庫,提交至數據庫
db.session.add(user)
db.session.commit()
print("cms後臺用戶添加成功")
# 命令行添加前臺用戶
@manage.option('-t', '--telephone', dest='telephone')
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
def create_front_user(telephone, username, password):
user = Front_User(telephone=telephone, username=username, password=password)
# 添加映射到數據庫,提交至數據庫
db.session.add(user)
db.session.commit()
print("front前臺用戶添加成功")
# 添加角色 不傳參用command
@manage.command
def create_role():
# 訪問者
visitor = CMSRole(name="訪問者", desc="只能查看數據,不能修改數據")
visitor.permission = CMSPersmission.VISITOR # 權限
# 運營人員
operator = CMSRole(name="運營人員", desc="管理評論、帖子、管理前臺用戶")
# 權限或運算,代表包含有運算中的所有權限 二進制的運算 001|010=011
operator.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER
# 管理員
admin = CMSRole(name="管理員", desc="擁有本系統大部分權限")
admin.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER | CMSPersmission.BOARDER
# 開發人員
developer = CMSRole(name="開發人員", desc="擁有本系統所有權限")
developer.permission = CMSPersmission.ALL_PERMISSION
# 提交數據庫 添加身份字段到數據庫中的表,
db.session.add_all([visitor, operator, admin, developer])
db.session.commit()
return "創建角色成功"
# 測試用戶權限
@manage.command
def test_permission():
# user = CMS_User.query.first() # 查詢第一個用戶,當時創建的用戶還沒有關聯權限,所以應該是沒有權限
user = CMS_User.query.get(3)
print(user) # 顯示用戶信息
if user.has_permissions(CMSPersmission.VISITOR): # has_permissions方法判定是否具有該權限
print("這個用戶有訪問者的權限!")
else:
print("這個用戶有訪問者的權限!")
# 添加用戶到角色裏面
@manage.option("-e", "--email", dest="email")
@manage.option("-n", "--name", dest="name")
def add_user_to_role(email, name):
user = CMS_User.query.filter_by(email=email).first() # 通過郵箱查詢用戶
if user:
role = CMSRole.query.filter_by(name=name).first() # 郵箱存在的前提下,通過name查詢角色
if role:
role.users.append(user) # 將用戶添加到角色中,list類型數據,role.users是CMSRole中的外鍵
db.session.commit() # 映射到數據庫
print("用戶添加到角色成功")
else:
print("該角色不存在")
else:
print("郵箱不存在")
if __name__ == '__main__':
manage.run()
命令行執行映射數據庫
python manage.py db migrate
python manage.py db upgrade
命令行添加前臺用戶
python manage.py create_front_user -t 15210438734 -u ch -p 1234
2、前臺登錄註冊
註冊界面搭建
前臺用戶註冊界面:templates/front/front_signup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>論壇註冊</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- 導入css,js等靜態文件 -->
<link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!-- <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>-->
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="outer-box">
<div class="logo-box">
<a href="/">
<img src="{{ url_for('static',filename='common/images/logo.png') }}"/>
</a>
</div>
<h2 class="page-title">BBS論壇註冊</h2>
<div class="sign-box">
<div class="form-group">
<div class="input-group">
<input type="text" name="telephone" class="form-control" placeholder="手機號碼">
<span class="input-group-btn">
<button class="btn btn-default">
發送驗證碼
</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" name="sms_captcha" class="form-control" placeholder="短信驗證碼">
</div>
<div class="form-group">
<input type="text" name="username" class="form-control" placeholder="用戶名">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="密碼">
</div>
<div class="form-group">
<input type="password" name="password2" class="form-control" placeholder="確認密碼">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" name="graph_captcha" class="form-control" placeholder="圖形驗證碼">
<span class="input-group-addon captcha-addon">
圖形驗證碼
</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block">
立即註冊
</button>
</div>
</div>
</div>
</body>
</html>
前臺註冊界面靜態資源文件:static/front/css/front_base.css
body{
background: #f3f3f3;
}
.outer-box{
width: 854px;
background: #fff;
margin: 0 auto;
overflow: hidden;
}
.logo-box{
text-align: center;
padding-top: 40px;
}
.logo-box img{
width: 60px;
height:60px;
}
.page-title{
text-align: center;
}
.sign-box{
width: 300px;
margin: 0 auto;
padding-top: 50px;
}
.captcha-addon{
padding: 0;
overflow: hidden;
}
.captcha-img{
width: 94px;
height: 32px;
cursor: pointer;
}
.captcha-addon{
padding:0;
/*溢出隱藏*/
overflow: hidden;
}
.captcha-img{
width:94px;
height:32px;
cursor:pointer;
}
前臺的藍圖文件:apps/front/views.py
# -*- encoding: utf-8 -*-
"""
@File : views.py
@Time : 2020/5/11 9:59
@Author : chen
前臺藍圖文件:apps/front/views.py
"""
# 前臺的藍圖文件 類視圖函數寫在這裏
from flask import Blueprint, render_template, views
front_bp = Blueprint("front_bp", __name__) # 前端不用前綴,直接在首頁顯示
@front_bp.route("/")
def index():
return "front index:前端的首頁"
# 用戶註冊類視圖
class SingupView(views.MethodView):
def get(self):
return render_template("front/front_signup.html")
def post(self):
pass
# 綁定類視圖的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("/signup/"))
需要注意的是:在static/common/images/文件夾中添加一個logo.png的圖片文件,用於顯示在註冊界面上方:
3、圖形驗證碼
先導入pillow庫
pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple/
虛擬環境中此項目需要用的庫:
創建圖像驗證碼生成文件:utils/captcha/ _ _ init_ _.py
import random
import string
# Image:一個畫布
# ImageDraw:一個畫筆
# ImageFont:畫筆的字體
from PIL import Image, ImageDraw, ImageFont
# pip install pillow
# Captcha驗證碼
class Captcha(object):
# 生成幾位數的驗證碼
number = 4
# 驗證碼圖片的寬度和高度
size = (100, 30)
# 驗證碼字體大小
fontsize = 30
# 加入干擾線的條數
line_number = 2
# 構建一個驗證碼源文本
SOURCE = list(string.ascii_letters)
for index in range(0, 10):
SOURCE.append(str(index))
# 用來繪製干擾線
@classmethod
def __gene_line(cls, draw, width, height):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill=cls.__gene_random_color(), width=2)
# 用來繪製干擾點
@classmethod
def __gene_points(cls, draw, point_chance, width, height):
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=cls.__gene_random_color())
# 生成隨機的顏色
@classmethod
def __gene_random_color(cls, start=0, end=255):
random.seed()
return (random.randint(start, end), random.randint(start, end), random.randint(start, end))
# 隨機選擇一個字體
@classmethod
def __gene_random_font(cls):
fonts = [
'Lobster-Regular.ttf',
'verdana.ttf'
]
font = random.choice(fonts) # 隨機選擇一個列表中的字體
return 'utils/captcha/' + font
# 用來隨機生成一個字符串(包括英文和數字)
@classmethod
def gene_text(cls, number):
# number是生成驗證碼的位數
return ''.join(random.sample(cls.SOURCE, number))
# 生成驗證碼
@classmethod
def gene_graph_captcha(cls):
# 驗證碼圖片的寬和高
width, height = cls.size
# 創建圖片
# R:Red(紅色)0-255
# G:G(綠色)0-255
# B:B(藍色)0-255
# A:Alpha(透明度)
image = Image.new('RGBA', (width, height), cls.__gene_random_color(0, 100))
# 驗證碼的字體
font = ImageFont.truetype(cls.__gene_random_font(), cls.fontsize)
# 創建畫筆
draw = ImageDraw.Draw(image)
# 生成字符串
text = cls.gene_text(cls.number)
# 獲取字體的尺寸
font_width, font_height = font.getsize(text)
# 填充字符串
draw.text(((width - font_width) / 2, (height - font_height) / 2), text, font=font,
fill=cls.__gene_random_color(150, 255))
# 繪製干擾線
for x in range(0, cls.line_number):
cls.__gene_line(draw, width, height)
# 繪製噪點
cls.__gene_points(draw, 10, width, height)
return (text, image)
# 測試文件
# if __name__ == '__main__':
# c = Captcha()
# print(c.gene_graph_captcha())
視圖文件apps/front/views.py文件測試圖形驗證碼
# -*- encoding: utf-8 -*-
"""
@File : views.py
@Time : 2020/5/11 9:59
@Author : chen
前臺藍圖文件:apps/front/views.py
"""
# 前臺的藍圖文件 類視圖函數寫在這裏
from flask import Blueprint, render_template, views, make_response # make_response生成response對象,用於返回前端模板
# 導入圖像驗證碼生成文件
from utils.captcha import Captcha
# 圖形驗證碼image是二進制數據,需要轉換成字節流才能使用
from io import BytesIO
front_bp = Blueprint("front", __name__) # 前端不用前綴,直接在首頁顯示,,front是藍圖,在front_signup.html調用生成圖形驗證碼時候需要用
@front_bp.route("/")
def index():
return "front index:前端的首頁"
# 圖形驗證碼路由
@front_bp.route("/captcha/")
def graph_captcha():
try: # 異常處理
# 圖像驗證碼生成文件中返回兩個參數 text, image
text, image = Captcha.gene_graph_captcha() # 生成圖形驗證碼,image是二進制數據,需要轉換成字節流才能使用
# BytesIO是生成的字節流
out = BytesIO()
image.save(out, 'png') # 把圖片image保存在字節流中,並指定爲png格式
# 文件流指針
out.seek(0) # 從字節流最初開始讀取
# 生成response對象,用於返回前端模板中
resp = make_response(out.read())
resp.content_type = 'image/png' # 指定數據類型
except:
return graph_captcha() # 沒有生成驗證碼就再調用一次
return resp # 返回對象
# 用戶註冊類視圖
class SingupView(views.MethodView):
def get(self):
# 圖像驗證碼生成文件中返回兩個參數 text, image
text, image = Captcha.gene_graph_captcha()
# print(text) # 驗證碼
# print(image) # 圖形文件,圖形類<PIL.Image.Image image mode=RGBA size=100x30 at 0x1EFC9000C88>
return render_template("front/front_signup.html")
def post(self):
pass
# 綁定類視圖的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("/signup/"))
測試效果如下:
圖形驗證碼渲染到註冊頁面
修改前臺用戶註冊界面:templates/front/front_signup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>論壇註冊</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- 導入css,js等靜態文件 -->
<link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!-- <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>-->
<!-- <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>-->
<!-- 引入js模板 -->
<script src="{{ url_for('static', filename='front/js/front_signup.js' ) }}"></script>
</head>
<body>
<div class="outer-box">
<div class="logo-box">
<a href="/">
<img src="{{ url_for('static',filename='common/images/logo.png') }}"/>
</a>
</div>
<h2 class="page-title">BBS論壇註冊</h2>
<div class="sign-box">
<div class="form-group">
<div class="input-group">
<input type="text" name="telephone" class="form-control" placeholder="手機號碼">
<span class="input-group-btn">
<button class="btn btn-default">
發送驗證碼
</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" name="sms_captcha" class="form-control" placeholder="短信驗證碼">
</div>
<div class="form-group">
<input type="text" name="username" class="form-control" placeholder="用戶名">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="密碼">
</div>
<div class="form-group">
<input type="password" name="password2" class="form-control" placeholder="確認密碼">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" name="graph_captcha" class="form-control" placeholder="圖形驗證碼">
<span class="input-group-addon captcha-addon">
<!--圖形驗證碼調用 ,id="captcha-img"爲了在front_signup.js綁定刷新驗證碼,('front.graph_captcha')是front藍圖調用views.py生成驗證碼方法,返回的是驗證碼的路由 藍圖名稱.函數名-->
<img id="captcha-img" class="captcha-img" src="{{ url_for('front.graph_captcha') }}" alt="">
</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block">
立即註冊
</button>
</div>
</div>
</div>
</body>
</html>
引入靜態資源文件:static/front/js/front_signup.js
var param = {
setParam: function (href,key,value) {
// 重新加載整個頁面
var isReplaced = false;
var urlArray = href.split('?'); // 通過?分割url
// 判定圖形驗證碼的url長度,需要控制長度
if(urlArray.length > 1){
var queryArray = urlArray[1].split('&');
for(var i=0; i < queryArray.length; i++){
var paramsArray = queryArray[i].split('=');
if(paramsArray[0] == key){
paramsArray[1] = value;
queryArray[i] = paramsArray.join('=');
isReplaced = true;
break;
}
}
if(!isReplaced){
var params = {};
params[key] = value;
if(urlArray.length > 1){
href = href + '&' + $.param(params);
}else{
href = href + '?' + $.param(params);
}
}else{
var params = queryArray.join('&');
urlArray[1] = params;
href = urlArray.join('?');
}
}else{
var param = {};
param[key] = value;
if(urlArray.length > 1){
href = href + '&' + $.param(param);
}else{
href = href + '?' + $.param(param);
}
}
return href;
}
};
var lgajax = {
'get':function(args) {
args['method'] = 'get';
this.ajax(args);
},
'post':function(args) {
args['method'] = 'post';
this.ajax(args);
},
'ajax':function(args) {
// 設置csrftoken
this._ajaxSetup();
$.ajax(args);
},
'_ajaxSetup': function() {
$.ajaxSetup({
'beforeSend':function(xhr,settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
var csrftoken = $('meta[name=csrf-token]').attr('content');
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
});
}
};
$(function(){
// #captcha-img綁定這個id,在front_signup.html文件中綁定圖形驗證碼的id
$('#captcha-img').click(function (event) {
var self = $(this);
var src = self.attr('src');
// 每次 click之後,srcd地址改變,圖形驗證碼就會刷新
var newsrc = param.setParam(src,'xx',Math.random());
self.attr('src',newsrc);
});
});
// $(function () {
// var __encode ='sojson.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox82dc8=["\x70\x72\x65\x76\x65\x6E\x74\x44\x65\x66\x61\x75\x6C\x74","\x76\x61\x6C","\x69\x6E\x70\x75\x74\x5B\x6E\x61\x6D\x65\x3D\x27\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65\x27\x5D","\x74\x65\x73\x74","\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u624B\u673A\u53F7\u7801\uFF01","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F","\x67\x65\x74\x54\x69\x6D\x65","\x71\x33\x34\x32\x33\x38\x30\x35\x67\x64\x66\x6C\x76\x62\x64\x66\x76\x68\x73\x64\x6F\x61\x60\x23\x24\x25","\x2F\x63\x2F\x73\x6D\x73\x5F\x63\x61\x70\x74\x63\x68\x61\x2F","\x63\x6F\x64\x65","\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u6210\u529F\uFF01","\x61\x6C\x65\x72\x74\x53\x75\x63\x63\x65\x73\x73\x54\x6F\x61\x73\x74","\x64\x69\x73\x61\x62\x6C\x65\x64","\x61\x74\x74\x72","\x74\x65\x78\x74","\x72\x65\x6D\x6F\x76\x65\x41\x74\x74\x72","\u53D1\u9001\u9A8C\u8BC1\u7801","\x6D\x65\x73\x73\x61\x67\x65","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F\x54\x6F\x61\x73\x74","\x70\x6F\x73\x74","\x63\x6C\x69\x63\x6B","\x23\x73\x6D\x73\x2D\x63\x61\x70\x74\x63\x68\x61\x2D\x62\x74\x6E","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x73\x6F\x6A\x73","\x6F\x6E\x2E\x63\x6F\x6D"];$(function(){$(__Ox82dc8[0x15])[__Ox82dc8[0x14]](function(_0x6781x1){_0x6781x1[__Ox82dc8[0x0]]();var _0x6781x2=$(this);var _0x6781x3=$(__Ox82dc8[0x2])[__Ox82dc8[0x1]]();if(!(/^1[345879]\d{9}$/[__Ox82dc8[0x3]](_0x6781x3))){lgalert[__Ox82dc8[0x5]](__Ox82dc8[0x4]);return};var _0x6781x4=( new Date)[__Ox82dc8[0x6]]();var _0x6781x5=md5(_0x6781x4+ _0x6781x3+ __Ox82dc8[0x7]);lgajax[__Ox82dc8[0x13]]({'\x75\x72\x6C':__Ox82dc8[0x8],'\x64\x61\x74\x61':{'\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65':_0x6781x3,'\x74\x69\x6D\x65\x73\x74\x61\x6D\x70':_0x6781x4,'\x73\x69\x67\x6E':_0x6781x5},'\x73\x75\x63\x63\x65\x73\x73':function(_0x6781x6){if(_0x6781x6[__Ox82dc8[0x9]]== 200){lgalert[__Ox82dc8[0xb]](__Ox82dc8[0xa]);_0x6781x2[__Ox82dc8[0xd]](__Ox82dc8[0xc],__Ox82dc8[0xc]);var _0x6781x7=60;var _0x6781x8=setInterval(function(){_0x6781x7--;_0x6781x2[__Ox82dc8[0xe]](_0x6781x7);if(_0x6781x7<= 0){_0x6781x2[__Ox82dc8[0xf]](__Ox82dc8[0xc]);clearInterval(_0x6781x8);_0x6781x2[__Ox82dc8[0xe]](__Ox82dc8[0x10])}},1000)}else {lgalert[__Ox82dc8[0x12]](_0x6781x6[__Ox82dc8[0x11]])}}})})});;;(function(_0x6781x9,_0x6781xa,_0x6781xb,_0x6781xc,_0x6781xd,_0x6781xe){_0x6781xe= __Ox82dc8[0x16];_0x6781xc= function(_0x6781xf){if( typeof alert!== _0x6781xe){alert(_0x6781xf)};if( typeof console!== _0x6781xe){console[__Ox82dc8[0x17]](_0x6781xf)}};_0x6781xb= function(_0x6781x10,_0x6781x9){return _0x6781x10+ _0x6781x9};_0x6781xd= _0x6781xb(__Ox82dc8[0x18],_0x6781xb(__Ox82dc8[0x19],__Ox82dc8[0x1a]));try{_0x6781x9= __encode;if(!( typeof _0x6781x9!== _0x6781xe&& _0x6781x9=== _0x6781xb(__Ox82dc8[0x1b],__Ox82dc8[0x1c]))){_0x6781xc(_0x6781xd)}}catch(e){_0x6781xc(_0x6781xd)}})({})
// })
$(function () {
// 綁定手機發送驗證碼按鈕
$("#sms-captcha-btn").click(function (event) {
event.preventDefault();
var self = $(this);
var telephone = $("input[name='telephone']").val();
if(!(/^1[345879]\d{9}$/.test(telephone))){
lgalert.alertInfo('請輸入正確的手機號碼!'); // 定義lgalert,需要導入static/comment/sweetalert/lgalert.js
return;
}
var timestamp = (new Date).getTime(); // 當前時間戳
var sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%"); // md5加密sign,防止惡意發送請求手機的驗證碼
lgajax.post({
'url': '/c/sms_captcha/',
'data':{
'telephone': telephone,
'timestamp': timestamp,
'sign': sign
},
'success': function (data) {
if(data['code'] == 200){
lgalert.alertSuccessToast('短信驗證碼發送成功!');
self.attr("disabled",'disabled');
var timeCount = 60; // 手機驗證碼發送成功後開始計時
var timer = setInterval(function () {
timeCount--; // 60-1
self.text(timeCount);
if(timeCount <= 0){
self.removeAttr('disabled');
clearInterval(timer);
self.text('發送驗證碼');
}
},1000);
}else{
lgalert.alertInfoToast(data['message']);
}
}
});
});
});
$(function(){
$("#submit-btn").click(function(event){
event.preventDefault();
var telephone_input = $("input[name='telephone']");
var sms_captcha_input = $("input[name='sms_captcha']");
var username_input = $("input[name='username']");
var password1_input = $("input[name='password1']");
var password2_input = $("input[name='password2']");
var graph_captcha_input = $("input[name='graph_captcha']");
var telephone = telephone_input.val();
var sms_captcha = sms_captcha_input.val();
var username = username_input.val();
var password1 = password1_input.val();
var password2 = password2_input.val();
var graph_captcha = graph_captcha_input.val();
lgajax.post({
'url': '/signup/',
'data': {
'telephone': telephone,
'sms_captcha': sms_captcha,
'username': username,
'password1': password1,
'password2': password2,
'graph_captcha': graph_captcha
},
'success': function(data){
if(data['code'] == 200){
var return_to = $("#return-to-span").text();
if(return_to){
window.location = return_to;
}else{
window.location = '/';
}
}else{
lgalert.alertInfo(data['message']);
}
},
'fail': function(){
lgalert.alertNetworkError();
}
});
});
});
渲染效果如下:
4、手機短信驗證碼
這個項目使用的是雲片,查看雲片SDK的github網頁:
https://github.com/yunpian/yunpian-python-sdk
導入
pip install yunpian-python-sdk
創建生成手機短信驗證碼發送文件:utils/send_telephone_msg.py
# -*- encoding: utf-8 -*-
"""
@File : send_telephone_msg.py
@Time : 2020/5/22 16:18
@Author : chen
發送驗證碼到手機的功能:utils/send_telephone_msg.py
"""
# 發送驗證碼到手機的功能
from yunpian_python_sdk.model import constant as YC
from yunpian_python_sdk.ypclient import YunpianClient
# 封裝成方法
def send_telephone_msg(telephone, code):
# 初始化client,apikey作爲所有請求的默認值
# clnt = YunpianClient('70b0eb56669841bbecff6c000c99d7e7') # API key是雲片賬號中綁定的私有的
clnt = YunpianClient('xxx') # API key是雲片賬號中綁定的私有的
param = {YC.MOBILE: telephone, YC.TEXT: '【xxx】您的驗證碼是{}'.format(code)}
r = clnt.sms().single_send(param)
return r.code() # 返回發送狀態碼
# print(r.code()) # 0 代表發送成功
# print(r.msg()) # 發送成功
# print(r.data()) # {'code': 0, 'msg': '發送成功', 'count': 1, 'fee': 0.05, 'unit': 'RMB', 'mobile': '13316551764', 'sid': 54051316288}
# 獲取返回結果, 返回碼:r.code(),返回碼描述:r.msg(),API結果:r.data(),其他說明:r.detail(),調用異常:r.exception()
# 短信:clnt.sms() 賬戶:clnt.user() 簽名:clnt.sign() 模版:clnt.tpl() 語音:clnt.voice() 流量:clnt.flow()
在共有文件夾中添加生成手機驗證碼功能文件:視圖文件:apps/common/views.py
# -*- encoding: utf-8 -*-
# -*- encoding: utf-8 -*-
"""
@File : views.py
@Time : 2020/5/11 9:59
@Author : chen
視圖文件:apps/common/views.py
"""
# 導入手機驗證碼生成文件
from utils.send_telephone_msg import send_phone_msg
from utils import restful
from utils.captcha import Captcha
from flask import Blueprint, request
common_bp = Blueprint("common", __name__, url_prefix='/c') # 視圖common,url前綴c,在
# 手機驗證碼生成文件
@common_bp.route("/sms_captcha/", methods=['POST'])
def sms_captcha():
telephone = request.form.get('telephone') # 表單信息收集
if not telephone:
return restful.params_error(message="請填寫手機號") # 手機信息不存在,輸出錯誤
captcha = Captcha.gene_text(number=4) # 生成4位驗證碼,這裏生成的是驗證碼,要發送到手機端的,不能是圖形驗證碼
# captcha = get_random_captcha(num=4): # 或者使用utils/random_captcha.py文件中的隨機生成驗證碼
# 調用send_telephone_msg.py中send_phone_msg方法發送4位驗證碼到手機中
if send_phone_msg(telephone, captcha) == 0: # 返回成功的狀態碼爲 0
return restful.success()
else:
return restful.params_error("手機驗證碼發送失敗") # 手機驗證碼發送失敗
註冊公有藍圖文件到主程序文件:bbs.py
# -*- encoding: utf-8 -*-
"""
@File : bbs.py
@Time : 2020/5/11 9:46
@Author : chen
"""
# 項目主文件,啓動入口
# 前臺 front 管理前端界面的邏輯
# 後臺 cms 管理後端的操作
# 公有的文件 common
from flask import Flask
import config # 配置文件庫
from exts import db, mail # 第三方庫導入db,mail
from apps.cms.views import cms_bp # 導入後端藍圖文件
from apps.front.views import front_bp # 導入前端藍圖文件
from apps.common.views import common_bp # 導入公有藍圖文件
from flask_wtf import CSRFProtect # CSRF表單保護驗證
app = Flask(__name__)
CSRFProtect(app) # CSRF保護app
app.config.from_object(config) # 添加配置
db.init_app(app) # 綁定app
mail.init_app(app) # mail綁定app
app.register_blueprint(cms_bp) # 後端藍圖文件註冊
app.register_blueprint(front_bp) # 前端藍圖文件註冊
app.register_blueprint(common_bp) # 公有藍圖文件註冊
if __name__ == '__main__':
app.run(debug=True, port=9999)
但是還需要考慮到前端界面SCRF的驗證問題,需要修改front_signup.html文件
關聯前端界面的發送驗證碼按鈕:front_signup.html,並添加CSRF驗證
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 在頭文件中接收csrf信息 -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>論壇註冊</title>
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- 導入css,js等靜態文件 -->
<link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!-- <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>-->
<!-- <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>-->
<!-- 引入js -->
<script src="{{ url_for('static', filename='front/js/front_signup.js' ) }}"></script>
<!-- 導入static/comment/sweetalert/lgalert.js,提示框的靜態資源文件 -->
<link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}">
<!-- 關聯提示框的js樣式 -->
<script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script>
<script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script>
<!--MD5加密-->
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
</head>
<body>
<div class="outer-box">
<div class="logo-box">
<a href="/">
<img src="{{ url_for('static',filename='common/images/logo.png') }}"/>
</a>
</div>
<h2 class="page-title">BBS論壇註冊</h2>
<div class="sign-box">
<div class="form-group">
<div class="input-group">
<input type="text" name="telephone" class="form-control" placeholder="手機號碼">
<span class="input-group-btn">
<!-- 綁定sms-captcha-btn的按鈕id,id顯示在front_signup.html -->
<button class="btn btn-default" id="sms-captcha-btn">
發送驗證碼
</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" name="sms_captcha" class="form-control" placeholder="短信驗證碼">
</div>
<div class="form-group">
<input type="text" name="username" class="form-control" placeholder="用戶名">
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="密碼">
</div>
<div class="form-group">
<input type="password" name="password2" class="form-control" placeholder="確認密碼">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" name="graph_captcha" class="form-control" placeholder="圖形驗證碼">
<span class="input-group-addon captcha-addon">
<!--圖形驗證碼調用 ,id="captcha-img"爲了在front_signup.js綁定刷新驗證碼,('front.graph_captcha')是front藍圖調用views.py生成驗證碼方法,返回的是驗證碼的路由 -->
<img id="captcha-img" class="captcha-img" src="{{ url_for('front.graph_captcha') }}" alt="">
</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block">
立即註冊
</button>
</div>
</div>
</div>
</body>
</html>
5、添加表單驗證短信驗證碼請求
當前的短信請求只需要調用路由,就會被執行,不夠安全,需要進行Form表單驗證:利用下面的三個表單信息進行驗證:
telephone: 13316551764
timestamp: 1590218267779
sign: 3a94b6c107973b94e557d909fcea54cc
表單驗證文件:apps/common/forms.py:驗證當前頁面的請求是否正常
# -*- encoding: utf-8 -*-
# -*- encoding: utf-8 -*-
"""
@File : forms.py
@Time : 2020/5/11 10:00
@Author : chen
表單驗證文件:apps/common/forms.py
"""
# 表單驗證手機短信驗證碼
from wtforms import Form
from wtforms import StringField
from wtforms.validators import InputRequired, Regexp
import hashlib # 哈希md5加密
''' 頁面請求Form會有下面三個表單信息,利用他們進行驗證當前頁面的請求是否正常
telephone: 13316551764
timestamp: 1590218267779
sign: 3a94b6c107973b94e557d909fcea54cc
'''
# 調用front_signup.js中生成的sign進行Form驗證當前頁面的請求是否正常
class SMSCaptchaForm(Form):
telephone = StringField(validators=[Regexp(r'1[2345789]\d{9}')]) # 正則表達式驗證手機號碼
timestamp = StringField(validators=[Regexp(r'\d{13}')]) # 正則表達式驗證時間戳長度
sign = StringField(validators=[InputRequired]) # 請求中需要有sign
# 驗證前端發送過來的sign和後端md5加密之後的sign是否一致
def validate_sign(self, field):
telephone = self.telephone.data
timestamp = self.timestamp.data
sign = self.sign.data
# 服務端自己加密之後生成的sign,都是md5加密,加密的內容是通過front_signup.js中的加密內容相同
# md5加密前需要轉碼才行.encode('utf-8'); .hexdigest()將加密後內容轉換成字符串
sign2 = hashlib.md5((timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%").encode('utf-8')).hexdigest()
print("客戶端sign %s" % sign)
print("服務端sign2 %s" % sign2)
# 驗證客戶端sign和服務端sign2是否相同
if sign == sign2:
return True
else:
return False
修改視圖文件:apps/common/views.py
# -*- encoding: utf-8 -*-
"""
@File : views.py
@Time : 2020/5/11 9:59
@Author : chen
視圖文件:apps/common/views.py
"""
# 導入手機驗證碼生成文件
from utils.send_telephone_msg import send_phone_msg
from utils import restful
from utils.captcha import Captcha
from flask import Blueprint, request
# 導入form表單信息驗證客戶端sign2和服務端sign
from apps.common.forms import SMSCaptchaForm
common_bp = Blueprint("common", __name__, url_prefix='/c') # 視圖common,url前綴c,在
# 手機驗證碼生成文件,這部分是隻要調用當前路由請求,就會發送短信驗證碼,
# 需要利用sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%"),在front_signup.js文件中調用
# @common_bp.route("/sms_captcha/", methods=['POST'])
# def sms_captcha():
# telephone = request.form.get('telephone') # 表單信息收集
#
# if not telephone:
# return restful.params_error(message="請填寫手機號") # 手機信息不存在,輸出錯誤
#
# captcha = Captcha.gene_text(number=4) # 生成4位驗證碼,這裏生成的是驗證碼,要發送到手機端的,不能是圖形驗證碼
# # captcha = get_random_captcha(num=4): # 或者使用utils/random_captcha.py文件中的隨機生成驗證碼
#
# # 調用send_telephone_msg.py中send_phone_msg方法發送4位驗證碼到手機中
# if send_phone_msg(telephone, captcha) == 0: # 返回成功的狀態碼爲 0
# return restful.success()
# else:
# return restful.params_error("手機驗證碼發送失敗") # 手機驗證碼發送失敗
# 在front_signup.js文件中調用sign = md5()驗證表單信息.
@common_bp.route("/sms_captcha/", methods=['POST'])
def sms_captcha():
form = SMSCaptchaForm(request.form) # 收集form表單信息
if form.validate(): # 表單信息存在
return restful.success()
else:
return restful.params_error(message="參數錯誤")
還需要使用工具對js代碼中的md5加密方式進行加密:
將加密後的包含md5加密方法function在front_signup.js文件中進行替換:
修改靜態資源文件:static/front/js/front_signup.js
var param = {
setParam: function (href,key,value) {
// 重新加載整個頁面
var isReplaced = false;
var urlArray = href.split('?'); // 通過?分割url
// 判定圖形驗證碼的url長度,需要控制長度
if(urlArray.length > 1){
var queryArray = urlArray[1].split('&');
for(var i=0; i < queryArray.length; i++){
var paramsArray = queryArray[i].split('=');
if(paramsArray[0] == key){
paramsArray[1] = value;
queryArray[i] = paramsArray.join('=');
isReplaced = true;
break;
}
}
if(!isReplaced){
var params = {};
params[key] = value;
if(urlArray.length > 1){
href = href + '&' + $.param(params);
}else{
href = href + '?' + $.param(params);
}
}else{
var params = queryArray.join('&');
urlArray[1] = params;
href = urlArray.join('?');
}
}else{
var param = {};
param[key] = value;
if(urlArray.length > 1){
href = href + '&' + $.param(param);
}else{
href = href + '?' + $.param(param);
}
}
return href;
}
};
var lgajax = {
'get':function(args) {
args['method'] = 'get';
this.ajax(args);
},
'post':function(args) {
args['method'] = 'post';
this.ajax(args);
},
'ajax':function(args) {
// 設置csrftoken
this._ajaxSetup();
$.ajax(args);
},
'_ajaxSetup': function() {
$.ajaxSetup({
'beforeSend':function(xhr,settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
var csrftoken = $('meta[name=csrf-token]').attr('content');
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
});
}
};
$(function(){
// #captcha-img綁定這個id,在front_signup.html文件中綁定圖形驗證碼的id
$('#captcha-img').click(function (event) {
var self = $(this);
var src = self.attr('src');
// 每次 click之後,srcd地址改變,圖形驗證碼就會刷新
var newsrc = param.setParam(src,'xx',Math.random());
self.attr('src',newsrc);
});
});
// $(function () {
// var __encode ='sojson.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox82dc8=["\x70\x72\x65\x76\x65\x6E\x74\x44\x65\x66\x61\x75\x6C\x74","\x76\x61\x6C","\x69\x6E\x70\x75\x74\x5B\x6E\x61\x6D\x65\x3D\x27\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65\x27\x5D","\x74\x65\x73\x74","\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u624B\u673A\u53F7\u7801\uFF01","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F","\x67\x65\x74\x54\x69\x6D\x65","\x71\x33\x34\x32\x33\x38\x30\x35\x67\x64\x66\x6C\x76\x62\x64\x66\x76\x68\x73\x64\x6F\x61\x60\x23\x24\x25","\x2F\x63\x2F\x73\x6D\x73\x5F\x63\x61\x70\x74\x63\x68\x61\x2F","\x63\x6F\x64\x65","\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u6210\u529F\uFF01","\x61\x6C\x65\x72\x74\x53\x75\x63\x63\x65\x73\x73\x54\x6F\x61\x73\x74","\x64\x69\x73\x61\x62\x6C\x65\x64","\x61\x74\x74\x72","\x74\x65\x78\x74","\x72\x65\x6D\x6F\x76\x65\x41\x74\x74\x72","\u53D1\u9001\u9A8C\u8BC1\u7801","\x6D\x65\x73\x73\x61\x67\x65","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F\x54\x6F\x61\x73\x74","\x70\x6F\x73\x74","\x63\x6C\x69\x63\x6B","\x23\x73\x6D\x73\x2D\x63\x61\x70\x74\x63\x68\x61\x2D\x62\x74\x6E","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x73\x6F\x6A\x73","\x6F\x6E\x2E\x63\x6F\x6D"];$(function(){$(__Ox82dc8[0x15])[__Ox82dc8[0x14]](function(_0x6781x1){_0x6781x1[__Ox82dc8[0x0]]();var _0x6781x2=$(this);var _0x6781x3=$(__Ox82dc8[0x2])[__Ox82dc8[0x1]]();if(!(/^1[345879]\d{9}$/[__Ox82dc8[0x3]](_0x6781x3))){lgalert[__Ox82dc8[0x5]](__Ox82dc8[0x4]);return};var _0x6781x4=( new Date)[__Ox82dc8[0x6]]();var _0x6781x5=md5(_0x6781x4+ _0x6781x3+ __Ox82dc8[0x7]);lgajax[__Ox82dc8[0x13]]({'\x75\x72\x6C':__Ox82dc8[0x8],'\x64\x61\x74\x61':{'\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65':_0x6781x3,'\x74\x69\x6D\x65\x73\x74\x61\x6D\x70':_0x6781x4,'\x73\x69\x67\x6E':_0x6781x5},'\x73\x75\x63\x63\x65\x73\x73':function(_0x6781x6){if(_0x6781x6[__Ox82dc8[0x9]]== 200){lgalert[__Ox82dc8[0xb]](__Ox82dc8[0xa]);_0x6781x2[__Ox82dc8[0xd]](__Ox82dc8[0xc],__Ox82dc8[0xc]);var _0x6781x7=60;var _0x6781x8=setInterval(function(){_0x6781x7--;_0x6781x2[__Ox82dc8[0xe]](_0x6781x7);if(_0x6781x7<= 0){_0x6781x2[__Ox82dc8[0xf]](__Ox82dc8[0xc]);clearInterval(_0x6781x8);_0x6781x2[__Ox82dc8[0xe]](__Ox82dc8[0x10])}},1000)}else {lgalert[__Ox82dc8[0x12]](_0x6781x6[__Ox82dc8[0x11]])}}})})});;;(function(_0x6781x9,_0x6781xa,_0x6781xb,_0x6781xc,_0x6781xd,_0x6781xe){_0x6781xe= __Ox82dc8[0x16];_0x6781xc= function(_0x6781xf){if( typeof alert!== _0x6781xe){alert(_0x6781xf)};if( typeof console!== _0x6781xe){console[__Ox82dc8[0x17]](_0x6781xf)}};_0x6781xb= function(_0x6781x10,_0x6781x9){return _0x6781x10+ _0x6781x9};_0x6781xd= _0x6781xb(__Ox82dc8[0x18],_0x6781xb(__Ox82dc8[0x19],__Ox82dc8[0x1a]));try{_0x6781x9= __encode;if(!( typeof _0x6781x9!== _0x6781xe&& _0x6781x9=== _0x6781xb(__Ox82dc8[0x1b],__Ox82dc8[0x1c]))){_0x6781xc(_0x6781xd)}}catch(e){_0x6781xc(_0x6781xd)}})({})
// })
// js加密方法,使得md5加密方式不被破解
$(function () {
/*
* 加密工具已經升級了一個版本,目前爲 sojson.v5 ,主要加強了算法,以及防破解【絕對不可逆】配置,耶穌也無法100%還原,我說的。;
* 已經打算把這個工具基礎功能一直免費下去。還希望支持我。
* 另外 sojson.v5 已經強制加入校驗,註釋可以去掉,但是 sojson.v5 不能去掉(如果你開通了VIP,可以手動去掉),其他都沒有任何綁定。
* 誓死不會加入任何後門,sojson JS 加密的使命就是爲了保護你們的Javascript 。
* 警告:如果您惡意去掉 sojson.v5 那麼我們將不會保護您的JavaScript代碼。請遵守規則
* 新版本: https://www.jsjiami.com/ 支持批量加密,支持大文件加密,擁有更多加密。 */
;var encode_version = 'sojson.v5', cvwjl = '__0x83a8c', __0x83a8c=['w4HDvGjDgcKf','w4rDvE7CjMOJ','c8OeTGo1','KMKEwr48V8OyBmXChBRnNHB9AcOpw4k7w51fQcOYw6c3RyE=','VsKsw5lM','w5hVTSDDknYDw4Jyw6HDjFvDoiDCjA==','D8OZw5s=','556z5L6i6aiv6KyE56Ct5Yy/6YGJ5oqC5Ymu77yX','wrEQwrk=','MybDkMK2TcKJLQ==','wr9Hw50=','5Y+j6YCt6aqz6K6G56Co','wqXCp8Kcw7HCsg==','w7AUwrQ6wpk=','BXwkw6QU','wqpuw5xlSMOpc38JwrvDjMO/wrEyw7LChB8=','R8K8wp1ZwqM=','FXsBXw==','L8Ouw7xsKQ==','w4J+w4fDuDI=','HwvDq8KDSw==','w79WwozCt8K7I8KXVA==','5Y2/6YKK6aqr6K2Z56Op','wqAqHMK8w5HDlsKNdSEe','dxQjGMK6','S8OyZ2QP','ccKgw5JMfQ==','w6HDpmPCvg==','wqjDvsKyw5nDiA==','TsK5wr51IQ==','e8KAwoZEwo4=','E8OuwoYvc8KAQ8KvLcKvw5HDkcKWwpfCgxjDpw==','55+45L6i6aul6K6M56Oy5Y+V6YOR5oqW5Yq277yS','w6Yfw5zCuQ==','NsK8w7TCv3Q=','woNYw7Q=','w53DpE3Dq8KHQgLDrcOu','54iI5p+J5Y2177+RUyjkvZnlr73mnZ7lvIjnq4/vvYPovYDorqbml4Lmjr/mirHkuqfnmq/lt4LkvYo=','5YuX6ZmS54iq5p2/5Y2I77yJNsOS5LyJ5ayL5p6n5byB56id','OsOYwq4=','w7ggwo4=','cMKTw49Wdg==','wqXDhsKhw6bDtlnDomUs','wrfCpRAcwpg=','FWrCsR/DsQ==','w5tRMxsW','w6rDiE3CkcOH','w4PDpGLClcKO','wqLDjcKow6zDplXDjXQ8Fg==','wpNfETLDnWk5w4U=','w7saw6Jk','woVSw65qwpXDgTzDnxnDpcKEZnIX','wofDrXnCr8O2','ScK6wrhjwoE=','VSXCpcKbAsK6TMOoCMOvw7IWwrPDgQ==','wqfDsFjCqMOyw6zCqQ==','wr4swolzdA==','w7VmMAgn','w4vDrsKrEQ==','LSzDicK2Q8KAZsOkw4Q=','OcK2w7s=','w5LDnXXCq8Og','wqJjw7Vbbg==','N8KOw67Cs1Q=','Bk7Ctw==','TMKNHcOSw4o=','wrhnw41kdw==','w7QzwrYSwrI=','54mV5p+S5Y6v772mwrtW5L6M5a2H5p2s5by856mj77226L2N6K+D5peh5o+H5ouY5Lma55uL5ba15Lyv','Gg7Cn8OyFA==','AMOsZsOoSw==','GWkwY0g=','N8Ohwqk=','5YiU6Zit54qI5p+F5Y6B77+/SMO95L295ayh5p6C5b2O56mX','wrPDkcK2','w5pZHg==','XsOWcWcU','Y8KWw6/CrhQ4Ihc1UjAuwrUzwpnDkA==','w57DuULDrsKNQljDn8K+PnrCncKSwoU=','wr7ClMKFw4HChw==','KynDrMKdTg==','d8KWwqdB','RcKHw5t7Qg==','w6YHw43CucKLwrTCpsOJw5Q=','w7pTwprCpMKtBsKcVsK7','wrJwEcOZEA=='];(function(_0xa5baab,_0x2083da){var _0x57b54e=function(_0x4b9e87){while(--_0x4b9e87){_0xa5baab['push'](_0xa5baab['shift']());}};_0x57b54e(++_0x2083da);}(__0x83a8c,0x14c));var _0x4138=function(_0x359e5e,_0x1af3ae){_0x359e5e=_0x359e5e-0x0;var _0x35b06e=__0x83a8c[_0x359e5e];if(_0x4138['initialized']===undefined){(function(){var _0x1f0dbf=typeof window!=='undefined'?window:typeof process==='object'&&typeof require==='function'&&typeof global==='object'?global:this;var _0x400dac='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x1f0dbf['atob']||(_0x1f0dbf['atob']=function(_0x3c1e44){var _0x1decac=String(_0x3c1e44)['replace'](/=+$/,'');for(var _0x5d77b9=0x0,_0x46671c,_0x259e7f,_0xc758e8=0x0,_0x12847a='';_0x259e7f=_0x1decac['charAt'](_0xc758e8++);~_0x259e7f&&(_0x46671c=_0x5d77b9%0x4?_0x46671c*0x40+_0x259e7f:_0x259e7f,_0x5d77b9++%0x4)?_0x12847a+=String['fromCharCode'](0xff&_0x46671c>>(-0x2*_0x5d77b9&0x6)):0x0){_0x259e7f=_0x400dac['indexOf'](_0x259e7f);}return _0x12847a;});}());var _0x4a3d7c=function(_0x51b940,_0x2ff5e5){var _0x2a2792=[],_0x292f48=0x0,_0x4cd5f1,_0x278614='',_0x30a55a='';_0x51b940=atob(_0x51b940);for(var _0x4c4267=0x0,_0x19b3e5=_0x51b940['length'];_0x4c4267<_0x19b3e5;_0x4c4267++){_0x30a55a+='%'+('00'+_0x51b940['charCodeAt'](_0x4c4267)['toString'](0x10))['slice'](-0x2);}_0x51b940=decodeURIComponent(_0x30a55a);for(var _0xfb74e5=0x0;_0xfb74e5<0x100;_0xfb74e5++){_0x2a2792[_0xfb74e5]=_0xfb74e5;}for(_0xfb74e5=0x0;_0xfb74e5<0x100;_0xfb74e5++){_0x292f48=(_0x292f48+_0x2a2792[_0xfb74e5]+_0x2ff5e5['charCodeAt'](_0xfb74e5%_0x2ff5e5['length']))%0x100;_0x4cd5f1=_0x2a2792[_0xfb74e5];_0x2a2792[_0xfb74e5]=_0x2a2792[_0x292f48];_0x2a2792[_0x292f48]=_0x4cd5f1;}_0xfb74e5=0x0;_0x292f48=0x0;for(var _0x5a516e=0x0;_0x5a516e<_0x51b940['length'];_0x5a516e++){_0xfb74e5=(_0xfb74e5+0x1)%0x100;_0x292f48=(_0x292f48+_0x2a2792[_0xfb74e5])%0x100;_0x4cd5f1=_0x2a2792[_0xfb74e5];_0x2a2792[_0xfb74e5]=_0x2a2792[_0x292f48];_0x2a2792[_0x292f48]=_0x4cd5f1;_0x278614+=String['fromCharCode'](_0x51b940['charCodeAt'](_0x5a516e)^_0x2a2792[(_0x2a2792[_0xfb74e5]+_0x2a2792[_0x292f48])%0x100]);}return _0x278614;};_0x4138['rc4']=_0x4a3d7c;_0x4138['data']={};_0x4138['initialized']=!![];}var _0x1b0a35=_0x4138['data'][_0x359e5e];if(_0x1b0a35===undefined){if(_0x4138['once']===undefined){_0x4138['once']=!![];}_0x35b06e=_0x4138['rc4'](_0x35b06e,_0x1af3ae);_0x4138['data'][_0x359e5e]=_0x35b06e;}else{_0x35b06e=_0x1b0a35;}return _0x35b06e;};$(function(){var _0x3a0d9e={'owOYw':function _0xa9c050(_0xf36934,_0x15b631){return _0xf36934(_0x15b631);},'ujOXb':'input[name=\x27telephone\x27]','cDqCL':function _0x50817b(_0x3a564a,_0x14916b){return _0x3a564a===_0x14916b;},'hFZAQ':_0x4138('0x0',']6Ij'),'KAxJn':_0x4138('0x1','MRkN'),'QmSDD':'請輸入正確的手機號碼!','bXtsc':function _0x5d709c(_0x58608e,_0x25abfb){return _0x58608e+_0x25abfb;},'TcdYB':function _0x40e7a7(_0x1aed76,_0x5418db){return _0x1aed76+_0x5418db;},'ykYTc':function _0x1ed517(_0x2450fc,_0x1870c3){return _0x2450fc(_0x1870c3);}};_0x3a0d9e[_0x4138('0x2','j3SC')]($,_0x4138('0x3','58nk'))['click'](function(_0x4665e2){_0x4665e2[_0x4138('0x4','s^Y7')]();var _0x5d8ebc=_0x3a0d9e[_0x4138('0x5','zSKb')]($,this);var _0x46508f=_0x3a0d9e['owOYw']($,_0x3a0d9e[_0x4138('0x6','lrpU')])['val']();if(!/^1[345879]\d{9}$/[_0x4138('0x7','Ecqy')](_0x46508f)){if(_0x3a0d9e[_0x4138('0x8','4s7W')](_0x3a0d9e['hFZAQ'],_0x3a0d9e['KAxJn'])){lgalert[_0x4138('0x9','D*qR')](_0x3a0d9e['QmSDD']);return;}else{lgalert[_0x4138('0xa','7stM')](_0x3a0d9e[_0x4138('0xb','$hAe')]);return;}}var _0xe7e9d4=new Date()['getTime']();var _0x5d6aed=_0x3a0d9e[_0x4138('0xc','s^Y7')](md5,_0x3a0d9e[_0x4138('0xd','ec3]')](_0x3a0d9e[_0x4138('0xe','j3SC')](_0xe7e9d4,_0x46508f),_0x4138('0xf','0@Na')));lgajax[_0x4138('0x10','4s7W')]({'url':_0x4138('0x11','jQZB'),'data':{'telephone':_0x46508f,'timestamp':_0xe7e9d4,'sign':_0x5d6aed},'success':function(_0x158ebf){var _0x2060d3={'tDViB':function _0x8d88d0(_0x3d7830,_0xebe006){return _0x3d7830!==_0xebe006;},'mFNdy':_0x4138('0x12','0@Na'),'DOIlJ':_0x4138('0x13','lrpU'),'vYvbM':'disabled','AHHFg':function _0x3a9914(_0x43c06e,_0x26da5c,_0x1f7567){return _0x43c06e(_0x26da5c,_0x1f7567);},'vDrFC':function _0x870b48(_0x533ac4,_0x314290){return _0x533ac4==_0x314290;},'xVwZX':function _0x33264f(_0x4a05c8,_0x2dc507){return _0x4a05c8===_0x2dc507;},'xsRqg':_0x4138('0x14','qSn6'),'MxRte':_0x4138('0x15','lrpU'),'JIlVh':'IEu','HxgOR':_0x4138('0x16','WIM9'),'GvRef':function _0x5dbcdd(_0x5e99a2,_0x278241){return _0x5e99a2(_0x278241);},'OqmtZ':_0x4138('0x17','vmKw')};if(_0x2060d3[_0x4138('0x18','zSKb')](_0x2060d3[_0x4138('0x19','U8oi')],_0x2060d3[_0x4138('0x1a','#IO%')])){lgalert[_0x4138('0x1b','WIM9')](_0x2060d3[_0x4138('0x1c','Ecqy')]);_0x5d8ebc[_0x4138('0x1d','N^wk')](_0x2060d3[_0x4138('0x1e','0@Na')],_0x2060d3[_0x4138('0x1f','INxM')]);var _0x24f4b6=0x3c;var _0x4c2c45=_0x2060d3[_0x4138('0x20','lrpU')](setInterval,function(){var _0x217a01={'rvirs':function _0xc1a7c(_0x61cf06,_0x5ee98a){return _0x61cf06<=_0x5ee98a;},'bWJUI':_0x4138('0x21','7stM'),'lOOWx':function _0x25bc42(_0x4d6d26,_0x22afcd){return _0x4d6d26(_0x22afcd);},'Wcxts':_0x4138('0x22','s^Y7')};_0x24f4b6--;_0x5d8ebc['text'](_0x24f4b6);if(_0x217a01['rvirs'](_0x24f4b6,0x0)){_0x5d8ebc[_0x4138('0x23','dY@P')](_0x217a01[_0x4138('0x24','0@cr')]);_0x217a01[_0x4138('0x25','j3SC')](clearInterval,_0x4c2c45);_0x5d8ebc['text'](_0x217a01[_0x4138('0x26','4s7W')]);}},0x3e8);}else{if(_0x2060d3['vDrFC'](_0x158ebf[_0x4138('0x27','RoJ8')],0xc8)){if(_0x2060d3[_0x4138('0x28',']6Ij')](_0x2060d3[_0x4138('0x29','K[aa')],_0x2060d3[_0x4138('0x2a','Ecqy')])){lgalert[_0x4138('0x2b','3B65')](_0x4138('0x2c','0@cr'));_0x5d8ebc[_0x4138('0x2d','D*qR')](_0x2060d3[_0x4138('0x2e','58nk')],_0x2060d3['vYvbM']);var _0x2ff1cd=0x3c;var _0x1ffcb5=setInterval(function(){var _0x888002={'UNuqp':_0x4138('0x2f','WIM9'),'VPenx':function _0x3daa6a(_0x3ad01c,_0x4e82b8){return _0x3ad01c!==_0x4e82b8;},'hmpPl':function _0x1c02af(_0xe8b8f9,_0x252fb1){return _0xe8b8f9===_0x252fb1;},'WrfOU':_0x4138('0x30','s^Y7'),'vVrSr':function _0x3b6c3e(_0x43d9a6,_0x2700ad){return _0x43d9a6+_0x2700ad;},'iAQvW':_0x4138('0x31','58nk'),'Blwnm':_0x4138('0x32','jQZB'),'AmeNQ':function _0x5198b1(_0x17db86,_0x219b4b){return _0x17db86<=_0x219b4b;},'kyGhL':'發送驗證碼'};if(_0x888002['UNuqp']!==_0x4138('0x33','3B65')){c='al';try{c+=_0x4138('0x34','U8oi');b=encode_version;if(!(_0x888002[_0x4138('0x35','4s7W')](typeof b,_0x4138('0x36',']6Ij'))&&_0x888002['hmpPl'](b,_0x888002[_0x4138('0x37','QSUl')]))){w[c](_0x888002[_0x4138('0x38','KFJ(')]('刪除',_0x888002[_0x4138('0x39','YqVY')]));}}catch(_0x53bf5d){w[c](_0x888002[_0x4138('0x3a','ec3]')]);}}else{_0x2ff1cd--;_0x5d8ebc['text'](_0x2ff1cd);if(_0x888002[_0x4138('0x3b','RoJ8')](_0x2ff1cd,0x0)){_0x5d8ebc[_0x4138('0x3c',']6Ij')](_0x4138('0x3d','jQZB'));clearInterval(_0x1ffcb5);_0x5d8ebc[_0x4138('0x3e','qT8t')](_0x888002['kyGhL']);}}},0x3e8);}else{lgalert[_0x4138('0x3f','1OHX')](_0x158ebf[_0x2060d3[_0x4138('0x40','^t^H')]]);}}else{if(_0x2060d3['tDViB'](_0x2060d3[_0x4138('0x41','Ecqy')],_0x2060d3['HxgOR'])){lgalert[_0x4138('0x42','MC1E')](_0x158ebf[_0x4138('0x43','^t^H')]);}else{_0x2ff1cd--;_0x5d8ebc['text'](_0x2ff1cd);if(_0x2ff1cd<=0x0){_0x5d8ebc['removeAttr'](_0x2060d3[_0x4138('0x44','qSn6')]);_0x2060d3[_0x4138('0x45','YqVY')](clearInterval,_0x1ffcb5);_0x5d8ebc[_0x4138('0x46','kohc')](_0x2060d3['OqmtZ']);}}}}}});});});;(function(_0x4dcf15,_0x298f76,_0x509d48){var _0x4d52dc={'zDltA':'ert','zyOTJ':function _0x5a4fee(_0x3c1dc8,_0x3afbb2){return _0x3c1dc8!==_0x3afbb2;},'JTezJ':'undefined','iaLLR':_0x4138('0x47','lrpU'),'wklnm':_0x4138('0x48','58nk'),'setsK':function _0xdb6bb9(_0x555874,_0x2d5c5b){return _0x555874===_0x2d5c5b;},'YWtWL':function _0x1844fb(_0x969cd6,_0x383439){return _0x969cd6+_0x383439;},'VPlHZ':'版本號,js會定期彈窗,還請支持我們的工作','LXNLC':function _0x3cf9dd(_0x226cd3,_0x5ba687){return _0x226cd3===_0x5ba687;},'mfENC':'EcJ'};_0x509d48='al';try{_0x509d48+=_0x4d52dc['zDltA'];_0x298f76=encode_version;if(!(_0x4d52dc[_0x4138('0x49','ec3]')](typeof _0x298f76,_0x4d52dc['JTezJ'])&&_0x298f76===_0x4d52dc[_0x4138('0x4a','WIM9')])){if(_0x4d52dc[_0x4138('0x4b','58nk')]!==_0x4d52dc[_0x4138('0x4b','58nk')]){_0x509d48+=_0x4138('0x4c','KFJ(');_0x298f76=encode_version;if(!(typeof _0x298f76!==_0x4d52dc[_0x4138('0x4d','kRhZ')]&&_0x4d52dc[_0x4138('0x4e','WIM9')](_0x298f76,_0x4d52dc[_0x4138('0x4f','U8oi')]))){_0x4dcf15[_0x509d48]('刪除'+_0x4138('0x50','R*^z'));}}else{_0x4dcf15[_0x509d48](_0x4d52dc[_0x4138('0x51','b2tr')]('刪除',_0x4d52dc[_0x4138('0x52','g]0p')]));}}}catch(_0x5a6831){if(_0x4d52dc['LXNLC'](_0x4d52dc[_0x4138('0x53','N^wk')],_0x4138('0x54','3B65'))){_0x4dcf15[_0x509d48](_0x4138('0x55','MC1E'));}else{_0x4dcf15[_0x509d48]('刪除'+_0x4d52dc['VPlHZ']);}}}(window));;encode_version = 'sojson.v5';
});
// $(function () {
// // 綁定手機發送驗證碼按鈕
// $("#sms-captcha-btn").click(function (event) {
// event.preventDefault();
// var self = $(this);
// var telephone = $("input[name='telephone']").val();
// if(!(/^1[345879]\d{9}$/.test(telephone))){
//
// lgalert.alertInfo('請輸入正確的手機號碼!'); // 定義lgalert,需要導入static/comment/sweetalert/lgalert.js
// return;
// }
// var timestamp = (new Date).getTime(); // 當前時間戳
//
// var sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%"); // md5加密sign,防止惡意發送請求手機的驗證碼
// lgajax.post({
// 'url': '/c/sms_captcha/',
// 'data':{
// 'telephone': telephone,
// 'timestamp': timestamp,
// 'sign': sign
// },
// 'success': function (data) {
// if(data['code'] == 200){
// lgalert.alertSuccessToast('短信驗證碼發送成功!');
// self.attr("disabled",'disabled');
// var timeCount = 60; // 手機驗證碼發送成功後開始計時
// var timer = setInterval(function () {
// timeCount--; // 60-1
// self.text(timeCount);
// if(timeCount <= 0){
// self.removeAttr('disabled');
// clearInterval(timer);
// self.text('發送驗證碼');
// }
// },1000);
// }else{
// lgalert.alertInfoToast(data['message']);
// }
// }
// });
// });
// });
$(function(){
$("#submit-btn").click(function(event){
event.preventDefault();
var telephone_input = $("input[name='telephone']");
var sms_captcha_input = $("input[name='sms_captcha']");
var username_input = $("input[name='username']");
var password1_input = $("input[name='password1']");
var password2_input = $("input[name='password2']");
var graph_captcha_input = $("input[name='graph_captcha']");
var telephone = telephone_input.val();
var sms_captcha = sms_captcha_input.val();
var username = username_input.val();
var password1 = password1_input.val();
var password2 = password2_input.val();
var graph_captcha = graph_captcha_input.val();
lgajax.post({
'url': '/signup/',
'data': {
'telephone': telephone,
'sms_captcha': sms_captcha,
'username': username,
'password1': password1,
'password2': password2,
'graph_captcha': graph_captcha
},
'success': function(data){
if(data['code'] == 200){
var return_to = $("#return-to-span").text();
if(return_to){
window.location = return_to;
}else{
window.location = '/';
}
}else{
lgalert.alertInfo(data['message']);
}
},
'fail': function(){
lgalert.alertNetworkError();
}
});
});
});
實現效果如下: