思路:
1、用户输入账号密码,请求登录api;
2、账号不存在,返回“账号不存在”信息;
3、账号存在,判断登录密码是否相等(加密后的密码),若不相等,返回“密码不正确”信息,若相等,生成用户token(用户令牌),并且设置登录时效性,将token返回给前端设置本地缓存;
4、访问任意api,检查是否登录/登录是否超时,若未登录或登录超时,则返回“Need Login”信息,用户需要重新登录。
models:
user.py
from flask_demo.app import db
from sqlalchemy import Column,ForeignKey
class User(db.Model):
__tablename__ = 'user'
id = Column(db.Integer, primary_key=True, autoincrement=True) #主键id
phone = Column(db.String(20), unique=True, nullable=False) #用户手机号
auth_key = Column(db.String(100), nullable=False) #加密后的密码
nick_name = Column(db.String(20)) #用户昵称
photo = Column(db.String(100)) #用户头像
views:
user.py
from flask_demo.app.models.user import User
from flask_demo.app import app,db
from flask import request
from flask_demo.utils.login import login_required
import json
import hashlib
import uuid
from flask_demo.utils import cache
@app.route('/login', methods=['GET','POST'])
def login():
if request.method == 'POST':
obj = request.get_json(force=True)
phone = obj["phone"]
passwd = obj["password"]
ls = User.query.filter(User.phone == phone).first()
if ls :
auth_key = ls.auth_key #数据库中加密的密码
password = pwd(passwd) #对用户登录的密码进行加密
if auth_key != password:
return json.dumps({"code": 200, "msg": "密码错误,请重新输入"}, indent=4, sort_keys=True, default=str,ensure_ascii=False)
else:
#登录成功,使用uuid4生成token
token = uuid.uuid4().hex
cache.savc_token(phone,token,20) #这边设置20s的登录超时时间
return json.dumps({"code": 200, "msg": "登录成功","token":token}, indent=4, sort_keys=True, default=str, ensure_ascii=False)
else:
return json.dumps({"code": 200, "msg": "账号不存在"}, indent=4, sort_keys=True, default=str, ensure_ascii=False)
@app.route('/test_msg', methods=['GET'])
@login_required #登录装饰器
def test_msg():
ls = User.query.all()
res = []
for x in ls:
temp = x.__dict__
del temp['_sa_instance_state']
res.append(temp)
return json.dumps({"code": 200, "data": res}, indent=4, sort_keys=True, default=str, ensure_ascii=False)
#将明文的txt转成MD5密文格式
def pwd(txt):
md5_ = hashlib.md5()
md5_.update(txt.encode('utf-8'))
md5_.update('@zjw@666#$*%'.encode('utf-8'))
return md5_.hexdigest()
utils公用组件:
cache.py
,通过redis进行登录缓存,并设置登录时效性
import redis
rdb = redis.Redis(host='localhost',port=6379,db=0)
def savc_token(userid,token ,expires):
rdb.set(userid,token,ex=expires)
def get_userid(token):
return rdb.get(token)
check_login.py
视图装饰器
Python 有一个非常有趣的功能:函数装饰器。这个功能可以使网络应用干净整洁。 Flask 中的每个视图都是一个装饰器,它可以被注入额外的功能。你可能已经用过了 route()
装饰器。但是,你有可能需要使用你自己的装饰器。 假设有一个视图,只有已经登录的用户才能使用。如果用户访问时没有登录,则会被 重定向到登录页面。这种情况下就是使用装饰器的绝佳机会。
检查登录装饰器
让我们来实现这个装饰器。装饰器是一个包装并替换另一个函数的函数。既然源函数 已经被替代,就需要记住:要复制源函数的信息到新函数中。可以用 functools.wraps()
处理这个事情。
from functools import wraps
from flask import request
from flask_demo.utils import rdb
import json
#检查登录
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
token = request.headers.get('token')
user = request.headers.get('user')
print(token,user)
if (token is None) or (not rdb.get(user)) or (token!=rdb.get(user).decode('utf-8')):
return json.dumps({'status': 1, 'msg': 'Need Login !'}), 401
return f(*args, **kwargs)
return decorated_function
测试结果:
用户账号不存在:
用户密码错误:
用户账号密码正确,登录成功,返回token:
访问其他api,输入错误的token:
访问其他api,输入正确的token,返回数据:
登录超时,访问其他api: