这里我是根据两个项目的实际情况做的总结,方法一(来自项目一)的登录用的是用户名(字符串)和密码,前后端不分离,用form表单传递数据;方法二用的是手机号和密码登录,前后端分离,以json格式传递数据,所以对登录数据的验证是不同的
方法一(利用插件 简单方便):
利用插件 flask_login 的 login_user, login_required, logout_user, current_user方法来实现用户的登入 检查登录 登出 获取当前登录用户。
首先 在view.py文件中做好导入工作:
form flask_login import login_user, login_required, logout_user, current_user
1 利用login_user实现登陆:
1 from flask_login import login_user, login_required, logout_user, current_user 2 from app.controls.auth.forms import LoginForm 3 4 @auth.route('/login', methods=['GET', 'POST']) 5 def login(): 6 form = LoginForm() # 账户 密码表单 7 if form.validate_on_submit(): 8 user = User.query.filter_by(username=form.username.data).first() 9 if user is not None and user.verify_password(form.password.data): 10 login_user(user, form.remember_me.data) 11 return redirect(request.args.get('next') or url_for('user_mgm.index')) 12 flash('用户名或密码错误') 13 form.username.data = '' 14 form.password.data = '' 15 return render_template('/auth/login.html', form=form)
2 利用logout_user实现登出:
@auth.route('/logout') @login_required def logout(): # 直接调用logout_user函数退出,里面实质封装了对session信息的清除 logout_user() return redirect(url_for('auth.login'))
3 直接利用login_required装饰器检查登录状态:
@auth.route('/changepwd', methods=['GET', 'POST']) @login_required # 检查登录状态 只有通过验证才能更改密码 def changepwd(): form = ChangePwdForm() if form.validate_on_submit(): if current_user.verify_password(form.oldpassword.data): current_user.password = form.newpassword.data db.session.add(current_user) db.session.commit() flash('密码修改成功!') return redirect(url_for('auth.login')) flash('原密码错误,请再次输入') form.oldpassword.data = '' form.newpassword.data = '' form.confirmedpassword.data = '' return render_template('auth/changepwd.html', name=current_user.name, grade=current_user.grade, form=form)
4 实现了1中的login_user(user, form.remember_me.data)方法,就可以在其他视图函数中直接通过current_user.name, current_user.grade来获取当前用户的属性,即current_user就代表当前的登录用户对象,它是通过login_user方法实现的。
方法二(手写 模仿插件中的原理)
1 登录核心内容: (1)验证用户输入的账号(手机号)和密码与数据库中的一致性 (2)通过验证,把用户数据保存到session
1 @api.route('/users', methods=['POST']) 2 def register(): 3 # 一. 获取参数 4 # request.data 5 # get_data获取的是字符串数据, 不利于后续的参数解析. 6 # 如果要用,需要配合json.loads()转换为字典格式的数据 7 # req_data = request.get_data() 8 # print req_data.get('mobile') 9 10 # get_json: 方便的获取JSON数据, 同时会自动转换为字典 11 req_json = request.get_json() 12 mobile = req_json.get('mobile') 13 sms_code = req_json.get('sms_code') 14 password = req_json.get('password') 15 16 # 二. 校验参数 17 # 1. 完整性 18 if not all([mobile, sms_code, password]): 19 # resp = { 20 # 'errno': RET.PARAMERR, 21 # 'errmsg': '参数不全,请重新输入' 22 # } 23 # return jsonify(resp) 24 25 # 建议以后使用这种写法, 简单一些 26 return jsonify(errno=RET.PARAMERR, errmsg='参数不全,请重新输入') 27 28 # 2. 验证手机号 --> import re.match(r"1[3456789]d{9}", value) 29 if not re.match(r"^1[3456789]d{9}$", mobile): 30 return jsonify(errno=RET.PARAMERR, errmsg='手机号不正确, 请输入正确手机号') 31 32 # 三. 逻辑处理 33 # 1. 从redis中获取数据对比 34 # 2. 判断用户是否注册过,没注册就创建并保存用户 35 # 3.(注册后直接登录)保存session 36 37 # 1.1 从redis中获取数据 38 try: 39 real_sms_code = redis_store.get('sms_code_%s' % mobile) 40 except Exception as e: 41 # logging.error(e) 42 # app.logger.error() logger模块默认已经集成到了app中, 但是没有智能提示不好用 43 current_app.logger.error(e) 44 return jsonify(errno=RET.DBERR, errmsg='redis读取失败') 45 46 # 1.2 判断数据是否为None 47 if real_sms_code is None: 48 return jsonify(errno=RET.NODATA, errmsg='短信验证码过期') 49 50 # 1.3 对比短信验证码 51 if real_sms_code != sms_code: 52 return jsonify(errno=RET.DATAERR, errmsg='短信验证码填写错误') 53 54 # 1.4 删除短信验证码 55 # 这里的1.3和1.4,与之前的短信验证码的步骤刚好相反. 56 # 短信验证码:1. 发短信要钱 2. 短信验证码接收可能时间过长或丢失(用户体验会不好) 57 try: 58 redis_store.delete('sms_code_%s' % mobile) 59 except Exception as e: 60 logging.error(e) 61 return jsonify(errno=RET.DBERR, errmsg='redis删除失败') 62 63 # 2. 判断用户是否注册过,没注册就创建并保存用户(密码保存,会在模型中做加密处理) 64 try: 65 user = User.query.filter_by(mobile=mobile).first() 66 except Exception as e: 67 logging.error(e) 68 return jsonify(errno=RET.DBERR, errmsg='mysql查询失败') 69 else: 70 if user is not None: 71 # 用户信息不是None, 说明已存在(已注册) 72 return jsonify(errno=RET.DATAEXIST, errmsg='用户手机号已经注册') 73 74 # 用户没有注册过 --> 创建用户对象并保存 75 user = User(name=mobile, mobile=mobile) 76 77 # pbkdf2:sha256:50000$ey5Pg8Ie$4fca7afb538b79c4d6c66a4c8c3cae23c192f02bfa97e8c51605d1fa6cd08773 78 user.password = password 79 80 # 用户1 123456 + 盐值 salt itcast 81 # 用户2 123456 + 盐值 salt hello 82 # i1t2c3a4s5t6 83 # h1e2l3l4056 84 # 希望有一个属性, 传入密码之后, 可以自动处理密码加密,并赋值给password_hash属性 85 # user.password_hash = password 86 87 try: 88 db.session.add(user) 89 db.session.commit() 90 except Exception as e: 91 # 还需要数据回滚 92 db.session.rollback() 93 logging.error(e) 94 return jsonify(errno=RET.DBERR, errmsg='mysql添加失败') 95 96 # 3.(注册后直接登录)保存session 97 try: 98 session['user_id'] = user.id 99 session['user_name'] = mobile 100 session['mobile'] = mobile 101 except Exception as e: 102 logging.error(e) 103 return jsonify(errno=RET.SESSIONERR, errmsg='session设置失败') 104 105 # 四. 返回数据 106 return jsonify(errno=RET.OK, errmsg='注册成功')
2 登出核心: 清除session
1 @api.route("/sessions", methods=["DELETE"]) 2 @login_required 3 def logout(): 4 """登出""" 5 # 清除session数据, csrf_token需要保留. 6 csrf_token = session['csrf_token'] 7 session.clear() 8 session['csrf_token'] = csrf_token 9 10 # 删除session的方式:3种 11 # 1.session.pop() 2. 和Django所学相同 3. session.clear()全部删除 12 13 return jsonify(errno=RET.OK, errmsg="OK")
3 重写login_required装饰器检查登录状态,并把current_user的功能放到应用上下文g变量中供其他视图函数使用
1 def login_required(func): 2 @wraps 3 def decorated_view(*args, **kwargs): 4 user_id = session.get('user_id') 5 if user_id is not None: 6 g.user_id = user_id 7 return func(*args, **kwargs) 8 else: 9 return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录') 10 return decorated_view
4 获取当前用户的功能封装在重写后的login_required中的g变量里,在其他视图函数中可以直接调用g.user_id获取当前登录用户的id