前端代码
html
<input type="text" class="form-control" name="phonecode" id="phonecode" placeholder="短信验证码" required>
<div class="phonecode input-group-addon">
<a class="phonecode-a" href="javascript:;" οnclick="sendSMSCode();">获取验证码</a>
<input type="text" class="form-control" name="imagecode" id="imagecode" placeholder="图片验证码" required>
<div class="input-group-addon image-code" οnclick="generateImageCode();"><img src=""></div>
</div>
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
var imageCodeId = "";
function generateUUID() {
var d = new Date().getTime();
if(window.performance && typeof window.performance.now === "function"){
d += performance.now(); //use high-precision timer if available
}
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
}
function generateImageCode() {
var picId = generateUUID();
$(".image-code img").attr("src", "/api/piccode?pre="+imageCodeId+"&cur="+picId);
imageCodeId = picId;
}
function sendSMSCode() {
$(".phonecode-a").removeAttr("onclick");
var mobile = $("#mobile").val();
if (!mobile) {
$("#mobile-err span").html("请填写正确的手机号!");
$("#mobile-err").show();
$(".phonecode-a").attr("onclick", "sendSMSCode();");
return;
}
var imageCode = $("#imagecode").val();
if (!imageCode) {
$("#image-code-err span").html("请填写验证码!");
$("#image-code-err").show();
$(".phonecode-a").attr("onclick", "sendSMSCode();");
return;
}
var data = {mobile:mobile, piccode:imageCode, piccode_id:imageCodeId};
$.ajax({
url: "/api/smscode",
method: "POST",
headers: {
"X-XSRFTOKEN": getCookie("_xsrf"),
},
data: JSON.stringify(data),
contentType: "application/json",
dataType: "json",
success: function (data) {
// data = {
// errcode
// errmsg
// }
if ("0" == data.errcode) {
var duration = 60;
var timeObj = setInterval(function () {
duration = duration - 1;
$(".phonecode-a").html(duration+"秒");
if (1 == duration) {
clearInterval(timeObj)
$(".phonecode-a").html("获取验证码");
$(".phonecode-a").attr("onclick", "sendSMSCode();")
}
}, 1000, 60)
} else {
$("#image-code-err span").html(data.errmsg);
$("#image-code-err").show();
$(".phonecode-a").attr("onclick", "sendSMSCode();")
if (data.errcode == "4002" || data.errcode == "4004") {
generateImageCode();
}
}
}
})
}
后端代码
生成验证码和短信4位数字
from utils.captcha.captcha import captcha
from libs.yuntongxun.SendTemplateSMS import ccp
class PicCodeHandler(BaseHandler):
"""图片验证码"""
def get(self):
"""获取图片验证码"""
pre_code_id = self.get_argument("pre", "")
cur_code_id = self.get_argument("cur")
# 生成图片验证码
name, text, pic = captcha.generate_captcha()
try:
if pre_code_id:
self.redis.delete("pic_code_%s" % pre_code_id)
# self.redis.setex(name, expries, value)
self.redis.setex("pic_code_%s" % cur_code_id, PIC_CODE_EXPIRES_SECONDS, text)
except Exception as e:
logging.error(e)
self.write("")
else:
self.set_header("Content-Type", "image/jpg")
self.write(pic)
class SMSCodeHandler(BaseHandler):
"""短信验证码"""
def post(self):
# 获取参数
mobile = self.json_args.get("mobile")
piccode = self.json_args.get("piccode")
piccode_id = self.json_args.get("piccode_id")
# 参数校验
# if mobile and piccode and piccode_id
if not all((mobile, piccode, piccode_id)):
return self.write(dict(errcode=RET.PARAMERR, errmsg="参数缺失"))
if not re.match(r"^1\d{10}$", mobile):
return self.write(dict(errcode=RET.PARAMERR, errmsg="手机号格式错误"))
# 获取图片验证码真实值
try:
real_piccode = self.redis.get("pic_code_%s" % piccode_id)
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.DBERR, errmsg="查询验证码错误"))
if not real_piccode:
return self.write(dict(errcode=RET.NODATA, errmsg="验证码过期"))
# 删除图片验证码
try:
self.redis.delete("pic_code_%s" % piccode_id)
except Exception as e:
logging.error(e)
if real_piccode.lower() != piccode.lower():
return self.write(dict(errcode=RET.DATAERR, errmsg="验证码错误"))
# 手机号是否存在检查
sql = "select count(*) counts from ih_user_profile where up_mobile=%s"
try:
ret = self.db.get(sql, mobile)
except Exception as e:
logging.error(e)
else:
if 0 != ret["counts"]:
return self.write(dict(errcode=RET.DATAEXIST, errmsg="手机号已注册"))
# 产生随机短信验证码
sms_code = "%06d" % random.randint(1, 1000000)
try:
self.redis.setex("sms_code_%s" % mobile, SMS_CODE_EXPIRES_SECONDS, sms_code)
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.DBERR, errmsg="数据库出错"))
# 发送短信验证码
try:
result = ccp.sendTemplateSMS(mobile, [sms_code, SMS_CODE_EXPIRES_SECONDS/60], 1)
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.THIRDERR, errmsg="发送短信失败"))
if result:
self.write(dict(errcode=RET.OK, errmsg="发送成功"))
else:
self.write(dict(errcode=RET.UNKOWNERR, errmsg="发送失败"))
注册
class RegisterHandler(BaseHandler):
"""注册"""
def post(self):
# 获取参数
mobile = self.json_args.get("mobile")
sms_code = self.json_args.get("phonecode")
password = self.json_args.get("password")
# 检查参数
if not all([mobile, sms_code, password]):
return self.write(dict(errcode=RET.PARAMERR, errmsg="参数不完整"))
if not re.match(r"^1\d{10}$", mobile):
return self.write(dict(errcode=RET.DATAERR, errmsg="手机号格式错误"))
# 如果产品对于密码长度有限制,需要在此做判断
# if len(password)<6
# 判断短信验证码是否真确
if "2468" != sms_code:
try:
real_sms_code = self.redis.get("sms_code_%s" % mobile)
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.DBERR, errmsg="查询验证码出错"))
# 判断短信验证码是否过期
if not real_sms_code:
return self.write(dict(errcode=RET.NODATA, errmsg="验证码过期"))
# 对比用户填写的验证码与真实值
# if real_sms_code != sms_code and sms_code != "2468":
if real_sms_code != sms_code:
return self.write(dict(errcode=RET.DATAERR, errmsg="验证码错误"))
try:
self.redis.delete("sms_code_%s" % mobile)
except Exception as e:
logging.error(e)
# 保存数据,同时判断手机号是否存在,判断的依据是数据库中mobile字段的唯一约束
passwd = hashlib.sha256(password + config.passwd_hash_key).hexdigest()
sql = "insert into ih_user_profile(up_name, up_mobile, up_passwd) values(%(name)s, %(mobile)s, %(passwd)s);"
try:
user_id = self.db.execute(sql, name=mobile, mobile=mobile, passwd=passwd)
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.DATAEXIST, errmsg="手机号已存在"))
# 用session记录用户的登录状态
session = Session(self)
session.data["user_id"] = user_id
session.data["mobile"] = mobile
session.data["name"] = mobile
try:
session.save()
except Exception as e:
logging.error(e)
self.write(dict(errcode=RET.OK, errmsg="注册成功"))
登陆
class LoginHandler(BaseHandler):
"""登录"""
def post(self):
# 获取参数
mobile = self.json_args.get("mobile")
password = self.json_args.get("password")
# 检查参数
if not all([mobile, password]):
return self.write(dict(errcode=RET.PARAMERR, errmsg="参数错误"))
if not re.match(r"^1\d{10}$", mobile):
return self.write(dict(errcode=RET.DATAERR, errmsg="手机号错误"))
# 检查秘密是否正确
res = self.db.get("select up_user_id,up_name,up_passwd from ih_user_profile where up_mobile=%(mobile)s",
mobile=mobile)
password = hashlib.sha256(password + config.passwd_hash_key).hexdigest()
if res and res["up_passwd"] == unicode(password):
# 生成session数据
# 返回客户端
try:
self.session = Session(self)
self.session.data['user_id'] = res['up_user_id']
self.session.data['name'] = res['up_name']
self.session.data['mobile'] = mobile
self.session.save()
except Exception as e:
logging.error(e)
return self.write(dict(errcode=RET.OK, errmsg="OK"))
else:
return self.write(dict(errcode=RET.DATAERR, errmsg="手机号或密码错误!"))
容联云通信发短信
from .CCPRestSDK import REST
import ConfigParser
import logging
#主帐号
accountSid= '8a216da86077dcd001607d2cbc3d02b2'
#主帐号Token
accountToken= '29dc1b3d316d47518df1519864988961'
#应用Id
appId='8a216da86077dcd001607d2cbc9202b8'
#请求地址,格式如下,不需要写http://
serverIP='app.cloopen.com';
#请求端口
serverPort='8883';
#REST版本号
softVersion='2013-12-26';
# 发送模板短信
# @param to 手机号码
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
# @param $tempId 模板Id
# def sendTemplateSMS(to,datas,tempId):
#
#
# #初始化REST SDK
# rest = REST(serverIP,serverPort,softVersion)
# rest.setAccount(accountSid,accountToken)
# rest.setAppId(appId)
#
# result = rest.sendTemplateSMS(to,datas,tempId)
# for k,v in result.iteritems():
#
# if k=='templateSMS' :
# for k,s in v.iteritems():
# print '%s:%s' % (k, s)
# else:
# print '%s:%s' % (k, v)
class CCP(object):
def __init__(self):
self.rest = REST(serverIP, serverPort, softVersion)
self.rest.setAccount(accountSid, accountToken)
self.rest.setAppId(appId)
@staticmethod
def instance():
if not hasattr(CCP, "_instance"):
CCP._instance = CCP()
return CCP._instance
def sendTemplateSMS(self, to, datas, tempId):
try:
result = self.rest.sendTemplateSMS(to, datas, tempId)
except Exception as e:
logging.error(e)
raise e
# print result
# for k, v in result.iteritems():
# if k == 'templateSMS':
# for k, s in v.iteritems():
# print '%s:%s' % (k, s)
# else:
# print '%s:%s' % (k, v)
if result.get("statusCode") == "000000":
return True
else:
return False
ccp = CCP.instance()
if __name__ == "__main__":
ccp = CCP.instance()
ccp.sendTemplateSMS("18516952650", ["1234", 5], 1)
判断登陆的装饰器
def required_login(fun):
# 保证被装饰的函数对象的__name__不变
@functools.wraps(fun)
def wrapper(request_handler_obj, *args, **kwargs):
# 调用get_current_user方法判断用户是否登录
if not request_handler_obj.get_current_user():
# session = Session(request_handler_obj)
# if not session.data:
request_handler_obj.write(dict(errcode=RET.SESSIONERR, errmsg="用户未登录"))
else:
fun(request_handler_obj, *args, **kwargs)
return wrapper
登出
class LogoutHandler(BaseHandler):
"""退出登录"""
@required_login
def get(self):
# 清除session数据
# sesssion = Session(self)
self.session.clear()
self.write(dict(errcode=RET.OK, errmsg="退出成功"))