flask框架-认证(二):使用请求中间件实现全局认证,部分路由不用,实现指定视图函数需要管理员权限,在request中存 用户信息

一、依赖包

alembic==1.8.1
click==7.1.2
Flask==1.1.4
Flask-Migrate==2.7.0
Flask-Script==2.0.6
Flask-SQLAlchemy==2.4.1
greenlet==2.0.1
importlib-metadata==5.0.0
importlib-resources==5.10.0
itsdangerous==1.1.0
Jinja2==2.11.3
Mako==1.2.4
MarkupSafe==2.0.1
PyMySQL==1.0.2
SQLAlchemy==1.3.0
typing_extensions==4.4.0
Werkzeug==1.0.1
zipp==3.10.0
Flask-RESTful==0.3.9
PyJWT==2.7.0
websocket-client==1.5.1
gevent==22.10.1
gevent-websocket==0.10.1
Flask-Caching==2.0.2
redis==4.5.5

二、配置token生成和解析

authen/token.py

from flask import request,g
import jwt
import time
from flask import jsonify
from functools import wraps #类装饰器
#导入用户表:这个根据用户自己来
from apps.user.models import UserModel


JWT_SECRET_KEY = '$#%^&&*(DFHJKUTYGHF112312' #加密的盐
ALGORITHM = 'HS256' #加密算法

#生成token
def jwt_encode_token(user_id:int,time_out=7*24*60*60):
    payload = {
        'user_id': user_id,
        'iat': time.time(), #产生token时的时间戳
        'exp': time.time() + time_out #token过期时间戳
    }
    token = jwt.encode(payload, JWT_SECRET_KEY, algorithm=ALGORITHM)
    return token

#解析token
def jwt_decode_token(token):
    secret = JWT_SECRET_KEY
    payload = jwt.decode(token,secret,algorithms=ALGORITHM)
    return payload


#需要管理员权限的视图,直接使用这个装饰器
def authen_admin_required_middle(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(request.user)
        if not request.user:
            return jsonify({'code':410,'msg':'请先登录'}),403
        if request.user.role_id !=1:
            return jsonify({'code':410,'msg':'没有管理员权限,无法操作'}),403
        #认证通过,执行视图函数
        return func(*args,**kwargs)
    return wrapper



if __name__ == '__main__':
    token = jwt_encode_token(78)

三、配置请求中间件

apps/middleware.py

from flask import request
from flask import jsonify
from authen.token import jwt_decode_token
import time
from apps.user.models import UserModel

#无须验证token的路由
IGNORE_TOKEN_URL={'/user/login/':['POST']}

#响应前使用的中间件【跨域】
def after_request(resp):
    # 给响应对象添加上
    #允许所有源来访问
    resp.headers['Access-Control-Allow-Origin'] = '*'
    #设置跨域访问的请求方式
    resp.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,OPTIONS,DELETE,PATCH'
    #设置跨域访问可以使用的请求头信息
    resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type,token'
    return resp

#请求前使用的中间件【认证中间件】
def before_request():
    method = request.method
    path = request.path
    if path in IGNORE_TOKEN_URL:
        #在这个路由当中的,且请求方式是要求的,就无须进行token认证
        if method in IGNORE_TOKEN_URL.get(path):
            return None
    #需要进行token认证
    token = request.cookies.get('token')
    if not token:
        token = request.headers.get('token')
    if not token:
        return jsonify({'code':410,'msg':'没有携带token'}),403
    try:
        payload = jwt_decode_token(token)
    except Exception as e:
        return jsonify({'code': 410, 'error': 'token有问题'}),403
    user_id = payload.get('user_id')
    exp = payload.get('exp')
    now = time.time()
    obj = UserModel.query.filter_by(id=user_id).first()
    if not obj:
        return jsonify({'code': 410, 'error': 'token有问题'}),403
    if exp < now:
        return jsonify({'code': 410, 'error': 'token已经过期了'}),403
    #把当前登录对象和token信息赋值给当前请求对象
    request.user = obj
    request.auth = token
    return None

四、注册中间件

apps/__init__.py

from flask import Flask

from flask import Flask

#导入蓝图
from apps.user.urls import user_bp


#导入配置字典
from settings import envs
#导入restful对象
from ext import api
#导入跨域对象
from ext import cors
#导入db对象
from ext import db
#导入cache
from ext import cache
#导入中间件
from apps.middleware import after_request  #响应前执行的中间件
from apps.middleware import before_request

cache_config_redis = {
    'CACHE_TYPE':'redis',
    'CACHE_REDIS_HOST':'127.0.0.1',
    'CACHE_REDIS_PORT':6637,
    # 'CACHE_REDIS_PASSWORD':'密码', #如果redis配置了密码
    'CACHE_REDIS_DB':0, #指定使用的redis的db,默认0-15
}

cache_config_mem = {
    'CACHE_TYPE':'simple'#使用内存作为cache
}

def create_app():
    #创建一个flask实例,传递__name__ ,是把当前文路径作为flask实例的根路径
    #static和templates都是创建在该路径下的
    app = Flask(__name__,static_folder='../static') #static目录位置是上层的static
    '基本配置'
    #导入配置从类中
    app.config.from_object(envs.get('default'))

    '中间件'
    #每次响应前都先设置好响应头,做好跨域
    app.after_request(after_request) #【1、使用中间件解决跨域】
    #执行请求处理前的中间件
    app.before_request(before_request)

    '拓展配置'
    #配置db对象 将db对象与app进行绑定,orm对象与app绑定
    db.init_app(app)
    #配置restful
    api.init_app(app)
    #配置跨域,supports_credentials=True 允许携带cookies等信息
    cors.init_app(app,supports_credentials=True) #【2、使用三方扩展解决跨域】
    #配置缓存
    cache.init_app(app=app,config=cache_config_redis)

    #蓝图注册
    app.register_blueprint(user_bp)

    return app

五、视图函数使用

from flask.views import MethodView
from flask import jsonify
from flask import request,g
from ext import db


#模型类
from . import models
#认证相关
from authen.token import jwt_encode_token,authen_admin_required_middle

class RoleView(MethodView):
    def get(self):
        objs = models.RoleModel.query.all()
        print(request.user)
        lis = []
        for obj in objs:
            lis.append({'id':obj.id,'name':obj.name})
        return jsonify({'code':200,'data':lis})
    #需要管理员权限的视图直接使用该装饰器
    @authen_admin_required_middle
    def post(self):
        print(g.user)
        name = request.form.get('name')
        if not name:
            return jsonify({'code':400,'error':'请携带上权限名'})

        obj = models.RoleModel.query.filter_by(name=name).first()
        if obj:
            return jsonify({'code':400,'error':f'{name} 权限已经存在了'})
        else:
            obj = models.RoleModel()
            obj.name=name
            db.session.add(obj)
            db.session.commit()
            return jsonify({'code':200,'msg':f'新增角色:{name} 成功'})

认证的大致逻辑:

1、每次请求到视图函数前,都会被拦截下来

2、不需要走认证流程的路由会直接放xing

3、需要走认证流程的路由,走完以后,可以在request中存用户信息,也可以在g中存用户信息,看个人习惯

权限大致逻辑:(依赖于认证流程)

1、认证流程通过后,就可以在request中获取用户信息

2、再对用户中角色进行判断,在操作权限即可

你可能感兴趣的:(flask,python)