基于Flask实现用户登录鉴权模块

概述

使用Flask实现一个简单的用户鉴权模块,后端数据库使用Sqlite,在业务上主要包括用户管理、权限管理。用户角色主要分为管理员(admin) 普通用户(user) 访客(guest)为求方便,不考虑注册功能实现,文末有源码下载链接)

业务分解

管理员(admin)

  • 权限:访问所有接口
  • 功能:
    • 添加user,guest用户
    • 删除user,guset用户
    • 查询所有用户

普通用户(user)

  • 权限:访问user权限接口
  • 功能:
    • 添加guest用户
    • 删除guest用户
    • 查询user,guest用户

访客(guest)

  • 权限:访问访客权限接口
  • 功能:无

业务流程图

在这里插入图片描述

业务实现

数据库设计

user表:(id,name,password , roles),用户表
role表:(id,name,permissions,users,角色表
permission表:(id,name,roles),权限表
user_role表:(id,user_id,role_id),用户角色关联表
role_permission表:(id,role_id,perssion_id)用户权限关联表
数据库模型如下:
在这里插入图片描述
数据表关系解析:

  • User 和 Role 之间的关系:
    • 一个 User 可以有多个 Role,一个 Role 也可以分配给多个 User。这种多对多关系通过 UserRole 表实现,这里为了方便,一个用户只对应一种角色
    • User 表中没有直接存储 Role 信息,而是通过 UserRole 表关联。
  • Role 和 Permission 之间的关系:
    • 一个 Role 可以拥有多个 Permission,一个 Permission 也可以赋予多个 Role。这种多对多关系通过 roles_permissions 表实现。这里为了方便,一个role只对应一种permission
    • Role 表中没有直接存储 Permission 信息,而是通过 roles_permissions 表关联。

代码实现

代码结构

在这里插入图片描述

创建基础配置(config.py)

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

import os

app = Flask(__name__)
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
DATABASE_PATH = os.path.join(BASE_DIR, 'data', 'users.db')
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DATABASE_PATH}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET_KEY') or '123456789'

db = SQLAlchemy(app)

初始化一个Flask对象,配置数据库路径,和jwt认证配置

构建数据库文件(model文件夹)

model/role.py

from config import db

#'role_permission.role_id' could not find table 'roles' with which to generate a foreign key to target column 'id'

class Role(db.Model):
    __tablename__ = 'role'
    id = db.Column(db.Integer, primary_key=True,autoincrement=True)
    name = db.Column(db.String(64), unique=True,nullable=False)
    #permissions = db.relationship('permission', secondary='role_permission', back_populates='roles')
    
class Permission(db.Model):
    __tablename__ = 'permission'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    
class RolePermission(db.Model):
    __tablename__ = 'role_permission'
    id = db.Column(db.Integer, primary_key=True)
    role_id = db.Column(db.Integer, db.ForeignKey('role.id',ondelete='CASCADE'),nullable=False)
    permission_id = db.Column(db.Integer, db.ForeignKey('permission.id', ondelete='CASCADE'))
    
Role.permissions = db.relationship('Permission', secondary='role_permission', backref='roles')

构建Role,Permisson,RolePermission等数据模型类,构建数据表之间的关系

model/user.py
from config import db

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(32), unique=True, nullable=False)
    password = db.Column(db.String(64))
    #roles = db.relationship('Role', secondary='user_role',back_populates='users')

class UserRole(db.Model):
    __tablename__ = 'user_role'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id',ondelete='CASCADE'), nullable=False)
    role_id = db.Column(db.Integer, db.ForeignKey('role.id',ondelete='CASCADE'), nullable=False)
    
User.roles = db.relationship('Role', secondary='user_role', backref='users')

构建User,UserRole数据模型

构建中间件(middleware文件夹)

middleware/auth.py
# middleware/auth.py
from functools import wraps
from flask import jsonify
from flask_jwt_extended import get_jwt_identity,jwt_required
from model.user import User

def role_required(role_name):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            current_user = get_jwt_identity()
            user = User.query.filter_by(name=current_user).first()
            if not user or not any(role.name == role_name for role in user.roles):
                return jsonify({"msg": "权限不足"}), 403
            return func(*args, **kwargs)
        return wrapper
    return decorator

def permission_required(permission_name):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            current_user = get_jwt_identity()
            user = User.query.filter_by(name=current_user).first()
            if not user or not any(permission.name == permission_name for role in user.roles for permission in role.permissions):
                return jsonify({"msg": "权限不足"}), 403
            return func(*args, **kwargs)
        return wrapper
    return decorator

# Admin权限
def admin_permission_required(func):
    @wraps(func)
    @jwt_required()
    def wrapper(*args, **kwargs):
        current_user = get_jwt_identity()
        user = User.query.filter_by(name=current_user).first()
        if not user or not any(permission.name == "Admin" for role in user.roles for permission in role.permissions):
            return jsonify({"msg": "权限不足"}), 403
        return func(*args, **kwargs)
    return wrapper

# User权限
def user_permission_required(func):
    def wrapper(*args, **kwargs):
        current_user = get_jwt_identity()
        user = User.query.filter_by(name=current_user).first()
        if not user or not any((permission.name == 'User' or permission.name == 'Admin') for role in user.roles for permission in role.permissions):
            return jsonify({"msg": "权限不足"}), 403
        return func(*args, **kwargs)
    return wrapper

role_required验证角色,permission_required验证用户权限,admin_permission_required验证是否是管理员权限,user_permission_required验证是否是普通用户权限

构建路由(routers文件夹)

routers/auth.py
# views/auth.py
from flask import Blueprint, jsonify, request
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from config import db
from model.user import User
from model.role import Role, Permission
from middleware.auth import role_required,permission_required

auth_bp = Blueprint('auth', __name__)


# 登录,获取access_token
@auth_bp.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    user = User.query.filter_by(name=username).first()
    if not user or user.password != password:
        return jsonify({"msg": "Bad username or password"}), 401
    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)


@auth_bp.route('/admin', methods=['GET'])
@jwt_required()
@role_required('Admin')
def admin_route():
    return jsonify({"msg": "Welcome Admin!"})

@auth_bp.route('/user', methods=['GET'])
@jwt_required()
@role_required('User')
def user_route():
    return jsonify({"msg": "Welcome User!"})

@auth_bp.route('/manage_users', methods=['GET'])
@jwt_required()
@permission_required('Admin')
def manage_users_route():
    return jsonify({"msg": "Welcome User Manager!"})

login用户登录,产出access_token,用于后续操作的验证,在后续请求中,header中添加Authorization:Bearer <access_token>,后面几个路由用来测试。

routers/action.py
from flask import Blueprint, jsonify, request
from flask_jwt_extended import jwt_required, get_jwt_identity
from sqlalchemy.orm import aliased
from config import db
from model.user import User
from model.role import Role
from middleware.auth import admin_permission_required, user_permission_required

action_bp = Blueprint('action', __name__)

#添加用户
# admin权限可以添加user,guest
# user权限可以添加guest
# guest无添加权限
@action_bp.route('/add', methods=['POST'])
@jwt_required()
def add_user():
    try:
        cur_name = get_jwt_identity()
        cur_user = User.query.filter_by(name=cur_name).first()
        if not cur_user:
            return jsonify({'code': 204, 'msg': '请先登录'})
        cur_role = cur_user.roles[0].name 
        data = request.get_json()
        username = data.get('username')
        password = data.get('password')
        role = data.get('role')
        if cur_role == 'Admin':
            if role in ('User','Guest'):
                tmp_role = Role.query.filter_by(name=role).first()
                user = User(name=username, password=password,roles=[tmp_role])
                db.session.add(user)
                db.session.commit()
                return jsonify({'code': 201, 'msg': '添加成功','data':{}})
            else:
                return jsonify({'code': 204, 'msg': '用户角色超出权限','data':{}})  
        elif cur_role == 'User':
            if role in ('Guest'):
                tmp_role = Role.query.filter_by(name=role).first()
                user = User(name=username, password=password,roles=[tmp_role])
                db.session.add(user)
                db.session.commit()
                return jsonify({'code': 201, 'msg': '添加成功','data':{}})
            else:
                return jsonify({'code': 204, 'msg': '用户角色超出权限','data':{}})
        else:
            return jsonify({'code': 204, 'msg': '用户角色超出权限','data':{}})
    except Exception as e:
        return jsonify({'code': 204, 'msg': str(e)})
    
# 删除用户
# admin权限可以删除user,guest
# user权限可以删除guest
# guest无删除权限
@action_bp.route('/delete', methods=['POST'])
@jwt_required()
def delete_user():
    try:
        data = request.get_json()
        username = data.get('username')
        user = User.query.filter_by(name=username).first()
        if user is None:
            return jsonify({'code': 204, 'msg': '用户不存在'})
        else:
            user_role = user.roles[0].name
            cur_name = get_jwt_identity()
            if cur_name == username:
                return jsonify({'code': 204, 'msg': '不能删除自己','data':{}})
            cur_user = User.query.filter_by(name=cur_name).first()
            if not cur_user:
                return jsonify({'code': 204, 'msg': '请先登录','data':{}})
            cur_role = cur_user.roles[0].name
            if cur_role == 'Admin' and user_role in ('User','Guest'):
                db.session.delete(user)
                db.session.commit()
                return jsonify({'code': 201, 'msg': '删除成功','data':{}})
            elif cur_role == 'User' and user_role == 'Guest':
                db.session.delete(user)
                db.session.commit()
                return jsonify({'code': 201, 'msg': '删除成功','data':{}})
            else:
                return jsonify({'code': 204, 'msg': '用户角色超出权限','data':{}})
    except Exception as e:
        return jsonify({'code': 204, 'msg': str(e)})


# 查询用户
# admin权限可以查询admin,user,guest
# user权限可以查询user,guest
@action_bp.route('/query', methods=['GET'])
@jwt_required()
def query_users():
    try:
        cur_name = get_jwt_identity()
        cur_user = User.query.filter_by(name=cur_name).first()
        if not cur_user:
            return jsonify({'code': 204, 'msg': '请先登录'})
        cur_role = cur_user.roles[0].name
        if cur_role == 'Admin':
            users = User.query.all()
        elif cur_role == 'User':
            users = User.query.join(User.roles).filter(Role.name != 'Admin').all()
            #users = User.query.filter(User.roles[0].name != 'Admin').all()
        else:
            return jsonify({'code': 204, 'msg': '用户角色超出权限'})
        res = []
        for u in users:
            tmp = {
                'id':u.id,
                'name':u.name,
                'role':u.roles[0].name
            }
            res.append(tmp)
        return jsonify({'code': 201, 'data': res,'msg': '查询成功'})
    except Exception as e:
        return jsonify({'code': 204, 'msg': str(e),'data':{}})

# admin模式
# 只有admin权限可以进入
@action_bp.route('/adminmode', methods=['POST'])
@admin_permission_required
def admin_mode():
    return jsonify({'code': 201, 'msg': '只有admin权限可以访问'})

# user模式
# admin,user权限可以进入
@action_bp.route('/usermode', methods=['POST'])
@user_permission_required
def user_mode():
    return jsonify({'code': 201, 'msg': '只有【admin,user】权限可以访问'})

# guest模式
# admin,user,guest权限可以进入
@action_bp.route('/guestmode', methods=['POST'])
def guest_mode():
    return jsonify({'code': 201, 'msg': '所有用户都可以访问'})

用户操作,添加,删除,查询,已经不同角色的权限测试

数据库生成,服务端构建(app.py,main.py)

app.py

from flask_jwt_extended import JWTManager
from config import app,db
from model.user import User
from model.role import Role,Permission
from routers.auth import auth_bp
from routers.action import action_bp
    #

def create_tables():
    with app.app_context():
        db.create_all()
        # 添加角色和权限
        if not Role.query.filter_by(name='Admin').first():
            admin_role = Role(name='Admin')
            admin_permission = Permission(name='Admin')
            
            user_role = Role(name='User')
            user_permission = Permission(name='User')
            
            guest_role = Role(name='Guest')
            guest_permission = Permission(name='Guest')
            
            admin_role.permissions.extend([admin_permission])
            user_role.permissions.extend([user_permission])
            guest_role.permissions.extend([guest_permission])
            
            #创建一个管理员
            user = User(name='admin', password='admin', roles=[admin_role])
                    
            db.session.add(admin_role)
            db.session.add(user_role)
            db.session.add(guest_role)
            db.session.add(admin_permission)
            db.session.add(user_permission)
            db.session.add(guest_permission)
            db.session.add(user)
            db.session.commit()
    
def init_app():
    create_tables()
    JWTManager(app)
    app.register_blueprint(auth_bp, url_prefix='/auth')
    app.register_blueprint(action_bp, url_prefix='/action')
    
    @app.route('/hello')
    def hello():
        return 'hello world'
    app.run(host='0.0.0.0', port=5000)
  • 创建数据库,并添加一个admin用户,本项目为只能创建了一个admin用户,如果需要多admin用户,请自行扩展
  • Flask对象添加路由和JWT配置初始化
main.py
from app import init_app

if __name__ == '__main__':
    init_app()

至此,项目完成了。我的系统是windows10,Python版本是3.11.7,项目工具版本如下

Flask == 3.0.3
Flask-JWT-Extended == 4.6.0
Flask-SQLAlchemy == 3.1.1

后记

一个简单的用户权限管理模块完成啦,由于本人主要从事后端开发,考虑到时间问题,所以前端没有实现,测试的话,你可以使用postman,或者自己实现前端,添加swagger等,如果测试中出现bug,请见谅。
源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值