登录/登出
后端逻辑编写
登录与登出都是对session这个资源的修改,因此url的名词资源就可以设置为sessions,编辑ihome/api_1_0/users.py
的视图文件
# ihome/api_1_0/users.py
from flask import request, jsonify, current_app, session
@api.route('/sessions', methods=['GET', 'POST', 'DELETE'])
def handle_sessions():
if request.method == 'POST':
# 登录
return login()
elif request.method == 'DELETE':
# 登出
return logout()
else:
# 获取当前登录用户
return get_login_user()
三个请求方式分别对应:登录POST
,登出DELETE
,获取当前登录用户GET
,分别编写对应的三个函数
# ihome/api_1_0/users.py
def login():
"""登录"""
# 接收数据
post_dict = request.get_json()
if not post_dict:
return jsonify(errno=RET.PARAMERR, errmsg='参数不能为空')
# 提取数据
phone = post_dict.get('phone')
password = post_dict.get('password')
# 校验数据
if not all([phone, password]):
return jsonify(errno=RET.PARAMERR, errmsg='参数不完整')
# 限制同一手机号一分钟内只能登陆5次
his_login_key = f'his_login_{phone}'
try:
# incr增加记录的次数,若不存在该key则会创建并默认值为1
count = redis_connect.incr(his_login_key)
if int(count) > constants.USER_LOGIN_COUNT:
return jsonify(errno=RET.REQERR, errmsg='一分钟内只能登录5次')
redis_connect.expire(his_login_key, constants.USER_LOGIN_EXPIRES)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg='获取登录记录异常')
# 校验是否已经登录过
if phone in session.values():
return jsonify(errno=RET.OK)
# 校验手机号是否注册过
try:
user = Users.query.filter_by(phone=phone).first()
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg='获取用户信息异常')
# 将用户名错误和密码错误放在一起返回,不要返回地太细节
if not user or not user.check_password_hash(password):
return jsonify(errno=RET.PWDERR, errmsg='用户名或密码不正确')
# 记录登录session
session['user_id'] = user.id
session['name'] = user.name
session['phone'] = phone
return jsonify(errno=RET.OK)
def logout():
"""登出"""
# 判断是否登录过
if not session.get('user_id'):
return jsonify(errno=RET.NODATA, errmsg='用户未登录')
# 清除session
session.pop('user_id')
session.pop('name')
session.pop('phone')
return jsonify(errno=RET.OK)
def get_login_user():
"""获取登录用户"""
# 判断是否登录过
name = session.get('name')
if name:
return jsonify(errno=RET.OK, data={'name': name})
return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')
前端逻辑编写-登录
在登录的js文件中添加登录按钮的逻辑,同样不使用form自带的表单提交功能,自定义表单的提交与返回操作
//login.js
$(document).ready(function() {
......
$(".form-login").submit(function(e){
//阻止默认的表单提交行为
e.preventDefault();
mobile = $("#mobile").val();
passwd = $("#password").val();
if (!mobile) {
$("#mobile-err span").html("请填写正确的手机号!");
$("#mobile-err").show();
return;
}
if (!passwd) {
$("#password-err span").html("请填写密码!");
$("#password-err").show();
return;
}
//发送ajax请求
//js对象数据转换为json格式
var postData = JSON.stringify({phone:mobile, password:passwd});
$.ajax({
url: 'api/v1.0/sessions',
type: 'post',
data: postData,
contentType: 'application/json',
headers: {'X-CSRFToken': getCookie('csrf_token')},
dataType: 'json',
success: function (resp) {
if(resp.errno == '0'){
//登录成功,跳转到首页
location.href='/index.html';
}else{
//登录失败
alert(resp.errmsg);
}
}
})
});
})
登录成功后,进入主页页面,在主页的右上角显示登录的用户名,编辑主页js文件,页面一加载就调用接口查询登录的用户
//index.js
$(document).ready(function(){
//发送ajax获取登录信息
$.get("/api/v1.0/sessions", function (resp) {
if (resp.errno == '0'){
//已登录,显示登录用户名
$(".user-info>.user-name").html(resp.data.name)
$(".user-info").show()
}else{
//未登录,显示登录注册框
$(".top-bar>.register-login").show();
}
}, "json")
前端逻辑编写-登出
点击主页的用户名,来到用户信息页面,最下面的'退出'按钮即为登出功能,设置该按钮的点击事件为logout()
编辑该界面的js
//my.js
function logout() {
$.ajax({
url: 'api/v1.0/sessions',
type: 'delete',
dataType: 'json',
headers: {
'X-CSRFToken': getCookies('csrf_token')
},
success: function (resp) {
if (resp.errno == '0'){
location.href='/'
}else{
alert(resp.errmsg)
}
}
})
}
登录验证装饰器
有些页面是需要用户登录才能访问的,比如用户信息页面,因此我们需要在这些页面的视图函数上添加登录的验证,这里使用装饰器的方式,在ihome/utils/commons.py
文件中添加该登录装饰器
# ihome/utils/commons.py
from flask import session, jsonify, g
from .response_codes import RET
import functools
def login_required(view_func):
"""登录验证装饰器"""
@functools.wraps(view_func)
def wrapper(*args, **kwargs):
# 判断登录状态
user_id = session.get('user_id')
if not user_id:
# 没有登录
return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')
# 已登录,将user保存在g对象中
try:
user = Users.query.get(user_id)
g.user = user
print(g.user)
except Exception as e:
current_app.logger.error(e)
return jsonify(errno=RET.DBERR, errmsg='获取用户异常')
# 执行视图函数
return view_func(*args, **kwargs)
return wrapper
注:
-
在session中若未获取到登录用户,则返回错误信息,用户未登录,若已登录,则执行下面的被装饰的视图函数
-
在装饰器内部添加
@functools.wraps(view_func)
能使被装饰函数的文件字符串等属性不会改变 -
在可以讲获取到的一些信息存在g变量中,这样在视图函数中就不需要重复查找, 这里将当前登录的User对象存在g对象中