登录
后端逻辑
-
获取参数
req_dict = request.get_json()
-
校验参数
if not all([a,b])
-
手机号格式
[user_ip = request.remote_addr # 用户的ip地址]()
-
判断错误次数是否超过限制
a=redis_store.get("a_%s" % user_ip ) if int(a) >= 5 :XXX
-
从数据库中根据手机号查询用户的数据对象(把和密码的校验放到一块,防止过详细的提示导致数据泄露)
-
密码对比验证
-
验证相同成功,保存登陆状态,在sessions中
-
models.py 中的检验密码的正确性
cheak_password_hash,
保存时设置次数,- redis 的incr函数连接:redis-py.readthedoces.io/en/latest 和 reidsdoc.com/index.html
验证失败记录错误参数 @api.route("/sessions", methods=["POST"]) def login(): """ 登陆: 参数 手机号,密码 return: """ # 获取参数 req_dict = request.get_json() mobile = req_dict.get("mobile") password = req_dict.get("password") # 检验参数 if not all([mobile, password]): return jsonify(errno=RET.PARAMERR, errmsg="参数不完整") # 判断手机的格式 if not re.match(r"1[345678]\d{9}", mobile): return jsonify(errno=RET.PARAMERR,errmsg="手机格式错误") # 判断错误请求次数是否超过限制,超过限制返回错误 # # redis 记录: "access_num_请求的IP": 次数 user_ip = request.remote_addr # 用户的ip地址 print(user_ip) try: access_nums = redis_store.get("access_num_%s"% user_ip) except Exception as e: current_app.logger.error(e) else: if access_nums is not None and int(access_nums) >= constants.LOGIN_ERROR_MAX_TIMES: return jsonify(errno=RET.REQERR, essmsg="请求次数过多,请稍后再试") # 从数据库根据手机号查询用户的数据对象 try: user = User.query.filter_by(mobile=mobile).first() except Exception as e: current_app.logger.error(e) return jsonify(error=RET.DATAERR,errmsg="获取参数失败") # 从数据库的密码和填写的密码对比验证,验证时要把用户名和密码一块验证 if user is None or not user.check_password(password): # 如果验证失败返回错误信息,记录错误次数 try: redis_store.incr("access_num_%s" % user_ip) redis_store.expire("access_num_%s"% user_ip, constants.LOGIN_ERROR_FORBID_TIME) except Exception as e: current_app.logger.error(e) return jsonify(error=RET.DATAERR, errmsg="密码或者用户名错误") # 如果验证相同成功,保存登录状态, 在session中 session["name"] = user.name session["mobile"] = user.mobile session["user_id"] = user.id return jsonify(errno=RET.OK, errmsg="登录成功")
用postman 验证后端测试
前端编写
//login.js 应用ajax
var data = {
mobile: mobile,
password: passwd
};
// 将data转为json字符串
var jsonData = JSON.stringify(data);
$.ajax({
url:"/api/v1.0/sessions",
type:"post",
data: jsonData,
contentType: "application/json",
dataType: "json",
headers:{
"X-CSRFToken":getCookie("csrf_token")
},
success: function (data) {
if (data.errno == "0") {
// 登录成功,跳转到主页
location.href = "/";
}
else {
// 其他错误信息,在页面中展示
$("#password-err span").html(data.errmsg);
$("#password-err").show();
}
}
});
登陆——登出
定义登陆验证装饰器
最简单的装饰器——《两层闭包》
定义的验证登陆状态的装饰器
闭包
概念:可以形象的把它理解为一个封闭的包裹,这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡。
def func(name):
def inner_func(age):
print 'name:', name, 'age:', age
return inner_func
bb = func('the5fire')
bb(26) # >>> name: the5fire age: 26
-
使用闭包
在python中很重要也很常见的一个使用场景就是装饰器,Python为装饰器提供了一个很友好的“语法糖”——@,简言之你在一个函数func上加上@decorator_func, 就相当于decorator_func(func)::
def decorator_func(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator_func # 等价于 decorator_func(func)
def func(name):
print 'my name is', name
在装饰器的这个例子中,闭包(wrapper)持有了外部的func这个参数,并 且能够接受外部传过来的参数,接受过来的参数在原封不动的传给func,并返回执行结果。
在内层调用时加上@functools.wraps(func) 这样可以改变内层函数的属性
import functools def login_required(func): @functools.wraps(func) def wrapper(*arg, **kwargs): pass return wrapper @login_required def itcast(): """itcast python""" pass # itcast -> wrapper print(itcast.__name__) # wrapper.__name__ print(itcast.__doc__) # wrapper.__doc__ TIP:不加 @login_required 装饰前print的结果时itcast()函数的名和说明文档,加上之后就变成wrapper内层函数的名字,但是一般希望改变装饰函数的属性结果,所以在内层函数之上 加上 @functools.wraps(func),这样不会改变装饰函数的结果