8. 为Lamb编写用户登录接口(带token)

简介: 上篇我们给Lamb引入了flask_sqlalchemy,接着我们就编写我们第一个接口---登录

前言


上篇我们给Lamb引入了flask_sqlalchemy,接着我们就编写我们第一个接口---登录。

知识准备

  • cookie和session
    传统的web软件,需要存储用户的登录信息。cookie和session是比较常见的模式,当然也是比较古老的模式。其中cookie主要依赖于客户端,可以简单的说是浏览器。可以说用户的登录信息,存储在浏览器的localstorage中。而session,顾名思义,它是将用户存储于服务器中。更好的解释请自行百度。


  • token
    token从广义说的话,是令牌的意思。我这里是json web token(JWT)的简写。简单的说,就是一种通过存储用户信息(以base64编码存储)然后需要的时候将之解析的方法。这样就达到你这个用户所有信息都被存储为一个字符串而且也看不到具体内容的目的。感兴趣的同学可以去看看~~

Python中的JWT

Python好像自带了jwt,我不是很确定。大家可以测试一下import jwt是否报错。

先给出我在Lamb/server/middleware/jwt.py中的实现

from datetime import timedelta, datetime
import jwt
from jwt.exceptions import ExpiredSignatureError
class UserToken(object):
    key = 'LambToken'
    expired_time = 3
    @staticmethod
    def get_token(data):
        new_data = dict({"exp": datetime.utcnow() + timedelta(hours=UserToken.expired_time)}, **data)
        return jwt.encode(new_data, key=UserToken.key).decode()
    @staticmethod
    def parse_token(token):
        try:
            return jwt.decode(token, key=UserToken.key)
        except ExpiredSignatureError:
            raise Exception("token已过期, 请重新登录")


首先介绍下吧,我封装了一个UserToken类:

  • key
    这个字段是一个保密的东西,我这里给大家暴露无遗。这个应该是在你数据的前后加上这个key再解码成base64,所以可以看到decode的时候也需要这个key。
  • expired_time
    这是token的过期时间,我这里设置的是3个小时。
  • get_token
    这里的data参数是我要存储的数据,其实就是user表的内容。
  • parse_token
    其实比较简单,但是需要注意的是,我这里只捕获了一种异常,可能还有token解析失败的异常,这里只是初版。为了适配我们的登录接口。

流程


我们这里设定的逻辑,可能会有缺陷。大概如下:

  1. 用户登录,服务器校验用户名密码是否正确;
  2. 如果不正确则返回用户名或密码错误的提示消息,如果正确,则将用户表里用户的数据通过token存储并返回给客户端,客户端随后只需要操作token,服务端解析token就知道是哪个用户操作的了。

开始

  • 首先我们要编写views层的内容Lamb/server/app/views/auth/User.py
from flask import Blueprint
from flask import jsonify
from flask import request
from ..auth.UserSchema import UserSchema
from ...controllers.auth.user import UserUtil
from ...utils.decorator import schema, token_expired
user = Blueprint("user", __name__, url_prefix="/user")
@user.route("/login", methods=("POST",))
#@schema(UserSchema.login_schema) 此处涉及到请求参数校验跳过
def login():
    data = request.get_json()
    user_info, token = UserUtil.login(data.get("username"), data.get("password"))
    if user_info is None:
        return jsonify(dict(code=500, msg="用户名或密码错误"))
    return jsonify(dict(code=200, msg="登录成功", token=token, **user_info))


  • 代码十分简单,定义了一个user蓝图,在其router下定义了一个/login接口,允许的请求方式为POST(这里是可以多选的)。随后获取请求json,我这里data.get('username')之所以没做判断,是因为上面的schema装饰器帮忙做了校验=。=,今天有点晚了,就先不细讲了。可以看到我调用了controllers/auth/user下的UserUtil类,完成登录操作。如果取出来的user_info为None,就返回用户名或密码错误。否则就返回token以及用户信息。
  • 完善login方法
from .. import User
from .. import Team
from ...middleware.jwt import UserToken
class UserUtil(object):
    @staticmethod
    def login(user, pwd):
        user_info, token = None, None
        man = User.query.filter_by(username=user, password=pwd).first()
        if man is not None:
            # 生成token
            user_info = UserUtil.get_dict(man)
            team_info = Team.query.filter_by(id=man.team_id).first               if team_info is None:
                # 没有team归属 列为默认权限
                user_info["permission"] = "user"
            return user_info, UserToken.get_token(user_info)
        return user_info, token
    @staticmethod
    def get_dict(user_obj):
        return {c.name: getattr(user_obj, c.name) for c in user_obj.__table__.columns if     
c.name != 'password'}


  • login为登录方法,可以看到这里先默认user_info和token为None,接着通过User.query.filter_by去查询username=user且password=pwd的第一条数据。这个方法如果没有查询到,会返回None。

接着判断man是不是None,如果不是None说明用户存在,接着去查询他在团队的权限,如果查询到他不属于任何团队,则给他一个默认权限。我这里的初衷是,权限以团队为单位。比如管理员团队,分配什么权限,当然我这里权限表还没设计好。

get_dict是将User类转为dict对象。

对自己的登录接口做接口测试


首先看自己的数据库的数据。

1.jpg

image.png

可以看到我的账号是wuranxu,密码是123。

启动服务以后,我们利用postman调用一下自己的接口。

正确的用户名密码

2.jpg

image.png

然后是错误的用户名密码

3.jpg

image.png

然后是字段不传

4.jpg

image.png

接着是字段类型不正确

5.jpg

image.png


总结

其实上述例子还是有挺多问题的,后续值得我们去优化。

  • http返回json code有很多种,我这里虽然作了规定。比如102、101、200、500等。但是这些很显然应该保存为常量,或者说以特定的错误形式返回出去。比如101是参数丢失,102是参数类型不正确。
  • 给用户的默认权限是user
    这里应该也是一套权限列表,user也应该以常量的形式存储起来。
  • 其实虽然每次登录token都会刷新,但是之前的那个token只要没有过期就仍然可用。
  • 日志和异常处理


对于错误的记录/处理几乎没有,这个很容易造成服务器崩溃。


这些都是值得优化的地方,我个人经验也比较有限。所以还是需要努力学习啊!*^*更新一次好累啊,晚安!!


相关文章
|
7月前
|
缓存 小程序 前端开发
【Uniapp】小程序携带Token请求接口+无感知登录方案2.0
【Uniapp】小程序携带Token请求接口+无感知登录方案2.0
208 0
|
4月前
|
SQL 数据安全/隐私保护
带token的多用户登录(注册直接粗暴解决)
带token的多用户登录(注册直接粗暴解决)
带token的多用户登录(注册直接粗暴解决)
|
7月前
|
缓存 小程序 NoSQL
【Uniapp】小程序携带Token请求接口+无感知登录方案
【Uniapp】小程序携带Token请求接口+无感知登录方案
124 0
|
9月前
|
存储 前端开发 NoSQL
|
9月前
|
前端开发 数据库 数据安全/隐私保护
DRF--认证和权限
DRF--认证和权限
|
9月前
|
前端开发
前端学习笔记202305学习笔记第二十二天-登录页校验封装和token封装1
前端学习笔记202305学习笔记第二十二天-登录页校验封装和token封装1
43 0
|
9月前
|
前端开发
前端学习笔记202305学习笔记第二十二天-登录页校验封装和token封装2
前端学习笔记202305学习笔记第二十二天-登录页校验封装和token封装2
42 0
|
12月前
|
数据库 Python
django drf 实现只有超级用户才能注册账号(涉及自定义权限permissions,获取token信息解析token信息)
django drf 实现只有超级用户才能注册账号(涉及自定义权限permissions,获取token信息解析token信息)