flask搭建平台实战教程三:接口编写及权限校验(前后端分离)
上一篇文章介绍了flask框架如何使用编写用户注册登录的功能
下一步编写查询用户列表接口,使用flask_restful的marshal方法来生成字段数据,加上之前编写的login_required
from flask import Blueprint,jsonify
from flask_restful import fields, marshal
from auth import login_required
from models.user import User
userbp = Blueprint('user',__name__,url_prefix="/user")
user_fields = {
'id': fields.Integer,
'username': fields.String,
'nickname': fields.String,
'email': fields.String,
'group_id': fields.Integer,
'_create_time': fields.DateTime(dt_format='iso8601'),
'update_time': fields.DateTime(dt_format='iso8601'),
}
@userbp.route("/list",methods=['GET'])
@login_required
def list():
users = User.getall()
return jsonify(code=201,msg="操作成功",data=marshal(users, user_fields))
请求时header中不加入Authorization字段就会验证失败
在header中添加Authorization,值为Bearer+空格+login返回数据的access_token,成功获取list接口数据
用户的增删改查都是一样的逻辑来写
from flask import Blueprint, jsonify
from flask_restful import fields, marshal, reqparse
from werkzeug.security import generate_password_hash
from auth import login_required
from models.user import User
userbp = Blueprint('user',__name__,url_prefix="/user")
user_fields = {
'id': fields.Integer,
'username': fields.String,
'nickname': fields.String,
'email': fields.String,
'group_id': fields.Integer,
'_create_time': fields.DateTime(dt_format='iso8601'),
'update_time': fields.DateTime(dt_format='iso8601'),
}
@userbp.route("/list",methods=['GET'])
@login_required
def list():
users = User.getall()
return jsonify(code=201,msg="操作成功",data=marshal(users, user_fields))
@userbp.route("/<uid>",methods=['GET'])
@login_required
def info(uid):
user = User.getone(uid)
return jsonify({
"code": 0,
"msg": "success",
"data": marshal(user, user_fields)
})
@userbp.route("/add",methods=['POST'])
@login_required
def add():
parser = reqparse.RequestParser()
parser.add_argument("username", type=str, required=True, help="username is required")
parser.add_argument("nickname", type=str, required=True, help="nickname is required")
parser.add_argument('password', required=True, type=str, help='password is required')
parser.add_argument('email', required=False, type=str)
parser.add_argument('group_id', required=False, type=int, default=2)
args = parser.parse_args()
if User.exist(username=args['username']):
return jsonify(code=400,msg="用户名已存在")
args.update({'password': generate_password_hash(args['password'], salt_length=8)})
User.add(args)
return jsonify(code=201,msg="添加成功")
@userbp.route("/edit",methods=['POST'])
@login_required
def edit():
parser = reqparse.RequestParser()
parser.add_argument("id", required=True, type=int, help="id is required")
parser.add_argument("username", type=str, required=True, help="username is required")
parser.add_argument("nickname", type=str, required=True, help="nickname is required")
parser.add_argument('email', required=False, type=str)
parser.add_argument('group_id', required=False, type=int)
args = parser.parse_args()
if not User.exist(id=args['id']):
return jsonify(code=201,msg="用户不存在")
User.edit(args)
return jsonify(code=201,msg="修改成功")
@userbp.route("/delete",methods=['GET'])
@login_required
def delete():
parser = reqparse.RequestParser()
parser.add_argument("id", required=True, type=int, location='args',help="id is required")
args = parser.parse_args()
if not User.exist(id=args['id']):
return jsonify(code=201,msg="用户不存在")
User.remove(args['id'])
return jsonify(code=201, msg="删除成功")
自定义权限校验
之后为接口添加分组的权限校验,根目录添加文件router.py,编写路由装饰器
routes ={} def route_meta(auth,endname):
# endname必须是{Blueprint注册的名字.方法名} def wrapper(func): routes.setdefault(f"{endname}", auth) return func return wrapper
在auth.py添加校验路由的装饰器
def group_required(fn): @wraps(fn) def wrapper(*args, **kwargs): if not current_user.isadmin: auth = routes.get(request.endpoint) groupid = current_user.group_id authed = Auth.exist(group_id=groupid,auth=auth,endpoint=request.endpoint) if not authed: return jsonify(code=10000,msg="权限不够,请联系超级管理员获得权限") return fn(*args,**kwargs) else: return fn(*args,**kwargs) return wrapper
在add接口方法加上这两个装饰器,route_meta第二个参数必须是{Blueprint注册的名字.方法名}
@route_meta("新增用户", "user.add") @userview.route("/add",methods=['POST']) @login_required @group_required def add():
......
再次请求add接口会提示没有权限
{
"code": 10000,
"msg": "权限不够,请联系超级管理员获得权限"
}
之后就需要为用户添加权限,权限关联到相关分组,所以添加修改分组接口
@groupbp.route("/edit",methods=['POST'])
@login_required
def edit():
parser = reqparse.RequestParser()
parser.add_argument("id", required=True, type=int, help="id is required")
parser.add_argument("name", required=True, type=str, help="name is required")
parser.add_argument("info", required=False, type=str)
args = parser.parse_args()
args['auths'] = request.json['auths']
sql = select(Group).where(Group.name==args['name'],Group.id!=args['id'])
if db.session.execute(sql).first():
return jsonify(code=10000,msg="分组名已存在")
Group.edit(args) # 需要重写edit
return jsonify(code=201,msg="操作成功")
由于修改分组需要添加、删除auth表的数据,所以要在models/group.py文件增加自己的edit方法
from models.auth import Auth
class Group:
......
@classmethod
def edit(cls, data):
group = cls.getone(data['id'])
group.name = data['name']
group.info = data['info']
for auth in group.auths:
Auth.remove(auth.id)
for item in data.get('auths'):
authobj = Auth.add(dict(endpoint=item,auth=routes[item]))
group.auths.append(authobj)
db.session.commit()
通过group/edit接口可以为admin分组增加权限
上面接口成功的话就为admin增加了add和edit这两个方法的权限,再添加和修改用户就能成功了
{
"code": 201,
"msg": "添加成功"
}
其它用户登录操作add、edit则会提示权限不够,当为delete方法添加group_required装饰器后,admin删除用户也会提示权限不够
至此flask用户管理及权限验证的后台接口基本后端框架就完成了,还有一些细节需要自己去改进,然后就是选择前端框架编写前端页面。