# lghome/api_1_0/passport.py
@api.route("/sessions", methods=["POST"])
def login():
"""
用户登录
:param: 手机号,密码
:return: json
"""
# 接收参数
request_dict = request.get_json()
mobile = request_dict.get('mobile')
password = request_dict.get('password')
print(mobile,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='手机号格式错误')
# 判断业务逻辑处理
# 从数据库查询手机号是否存在
try:
user = User.query.filter_by(mobile=mobile).first()
except Exception as e:
logging.error(e)
return jsonify(errno=RET.DBERR, errmsg='获取信息失败')
# 验证密码
if user is None or not user.check_pwd_hash(password):
return jsonify(errno=RET.DATAERR, errmsg='帐号密码不匹配')
# 保存登录状态
session['name'] = user.name
session['mobile'] = user.mobile
session['user_id'] = user.id
# 返回
return jsonify(errno=RET.OK, errmsg='登录成功')
4、算法优化
4.1 限制错误次数
如果不限制错误次数,会被恶意测试帐号密码
在一定时间内限制错误次数,超过限制次数后不允许登录
接收参数后,首先判断该前端IP防问是否超限
从redis中取出该IP已访问错误次数
错误次数不为空且大于等于限制次数时,直接返回错误
判断手机号是否存在(是否是注册用户)和密码错误是否正确,
如果错误,错误次数加1并存入redis中,加上有效时间
# 判断业务逻辑处理
# 判断错误次数是否超过限制,如果超过限制直接返回
# redis 用户IP地址:次数
user_ip = request.remote_addr
print(user_ip)
try:
access_nums=redis_store.get("access_nums_%s"%user_ip)
print("access_nums_%s"%user_ip,access_nums)
except Exception as e:
logging.error(e)
else:
if access_nums is not None and int(access_nums)>=constants.LOGIN_ERROR_MAX_TIMES:
return jsonify(errno=RET.REQERR, errmsg='错误次数太多,请稍后重试')
# 验证密码
# if user is None or not user.check_pwd_hash(password):
# return jsonify(errno=RET.DATAERR, errmsg='帐号密码不匹配')
if user is None or not user.check_pwd_hash(password):
try:
redis_store.incr("access_nums_%s" % user_ip)
redis_store.repire("access_nums_%s"%user_ip,constants.LOGIN_ERROR_FORBID_TIME)
except Exception as e:
logging.error(e)
return jsonify(errno=RET.DATAERR, errmsg='帐号密码不匹配')
4.2 保存错误次数时,使用管道
# 验证密码# if user is None or not user.check_pwd_hash(password):# return jsonify(errno=RET.DATAERR, errmsg='帐号密码不匹配')if user is None or not user.check_pwd_hash(password):
try:
# redis管道
pl = redis_store.pipeline()
pl.incr("access_nums_%s" % user_ip)
pl.repire("access_nums_%s"%user_ip,constants.LOGIN_ERROR_FORBID_TIME)
pl.execute()
except Exception as e:
logging.error(e)return jsonify(errno=RET.DATAERR, errmsg='帐号密码不匹配')
二、检查登录
用户登录后,刷新页面时,需要判断登录状态
有些页面只能在登录状态下使用,也需要判断登录状态
1、接口设计
1.1 请求方式
选项
方案
请求方法
GET
请求地址
/sessions
1.2 请求参数:无
1.3 响应结果:json
名字
类型
是否必须
说明
errno
字符串
是
错误代码
errmsg
字符串
是
错误内容
data
字典
否
{“name": 登录名}
2、登录检查接口定义
读取session用户信息
如果存在表示用户登录了,否则没登录
返回相应结果
def check_login():
"""
检查登录状态
:return: 用户的信息或者返回错误信息
"""
name = session.get('name')if name is not None:
return jsonify(errno=RET.OK, errmsg='true', data={
"name": name})
else:
return jsonify(errno=RET.SESSIONERR, errmsg='false')