Flask权限篇flask_principal

来源:互联网 发布:哈工大机械考研知乎 编辑:程序博客网 时间:2024/05/21 18:32

最近写CMDB的时候遇到了一个问题,那就是flask的权限问题,目前我了解到的Flask有3种方案进行权限管理的操作,

  1. Flask狗书中的十六进制的权限值来实现
  2. Flask-Security
  3. Flask Principal

    先跳过第一种,可能纯属是我技术的原因吧,我觉得有轮子不用反复造的想法,所以就跳过了第一种选择了后面2种框架的,其实Flask也是坑,flask_principal的作者已经不更新了,上次提交代码是5年前。。。。。。。Flask-Security这个也是一个大坑,你的user必须要有email,active 字段,而且你还必须用WTF,局限太大了所以放弃。 如果想要了解这里找到一篇不错的Blog传送门||同上。


我的项目结构树 CMDB/ ├─app/ │ ├─auth/ │ ├─main/ │ ├─static/ │ ├─templates/ │ ├─__init__.py │ ├─config.conf │ ├─models.py ├─manager.py

models.py,必要的模型关系user和role是多对多

from . import db,login_managerfrom flask_sqlalchemy import SQLAlchemyfrom werkzeug.security import generate_password_hash,check_password_hash#转换密码用到的库from flask_login import UserMixin#角色<-->用户,关联表roles_users = db.Table(    'role_user',    db.Column('user_id',db.Integer(),db.ForeignKey('user.id')),    db.Column('role_id',db.Integer(),db.ForeignKey('role.id')))#角色表class Role(db.Model):    __tablename__ = 'role'    id = db.Column(db.Integer(),primary_key=True)    name = db.Column(db.String(80),unique=True)    description = db.Column(db.String(255))    def __repr__(self):        return "<Role_id:{0}>".format(self.id)#用户表class User(db.Model,UserMixin):    __tablename__ = 'user'    id = db.Column(db.Integer(),primary_key=True)    username = db.Column(db.String(80),unique=True,nullable=False)    password_hash = db.Column(db.String(255))    #多对多关联    roles = db.relationship('Role',secondary='role_user',backref=db.backref('users',lazy='dynamic'))    def __repr__(self):        return "<User_id:{0}>".format(self.id)    # 这个方法是用于用户登录后返回数据库的ID到session中用来登录    @login_manager.user_loader    def load_user(id):        return User.query.get(int(id))    @property    def password(self):        raise AttributeError("密码不允许读取,请使用check_password_hash()进行验证密码")    #转换密码为hash存入数据库    @password.setter    def password(self,password):        self.password_hash = generate_password_hash(password)    #检查密码    def check_password_hash(self, password):        return check_password_hash(self.password_hash,password)

__init__.py,初始化

from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyfrom flask_login import LoginManager,current_user#权限认证需要的包from flask_principal import Principal, Permission, RoleNeed, identity_loaded# 初始化对象db = SQLAlchemy()login_manager = LoginManager()login_manager.session_protection = 'strong'  # 让session功能更加强壮login_manager.login_view = 'auth.login'  # 制定系统默认的登录页面login_manager.login_message = "请登录后再进行访问该页面!"#初始化Principalprincipals = Principal()#添加admin权限admin_permission = Permission(RoleNeed('admin'))# 工厂化def create_app():    app = Flask(__name__)    app.config.from_pyfile('config.conf')    db.init_app(app)    login_manager.init_app(app)    principals.init_app(app)    @identity_loaded.connect_via(app)    def on_identity_loaded(sender, identity):        #设置当前用户身份为login登录对象        identity.user = current_user        #添加UserNeed到identity user对象        if hasattr(current_user, 'id'):            identity.provides.add(UserNeed(current_user.id))        #每个Role添加到identity user对象,roles是User的多对多关联        if hasattr(current_user, 'roles'):            for role in current_user.roles:                identity.provides.add(RoleNeed(role.name))    # 注册蓝图    from .main import main as main_blueprint    from .auth import auth as auth_blueprint    app.register_blueprint(main_blueprint)    app.register_blueprint(auth_blueprint)    return app

1.identity_loaded :信号实现函数,需要访问 app 对象,所以在 create_app里面构造
2.identity_changed:用户身份变化时发送。
(其实有点复杂我也不是太理解,大家也可以看下其它大神的,我这里就当是一个demo好拉)


auth-views.py,login,logout都需要改变身份。

from . import authfrom .. import db,admin_permissionfrom ..models import Userfrom flask import redirect,render_template,request,url_for,flashfrom flask_login import login_user,logout_user,current_user,login_required# from flask_security import roles_required,current_user,logout_user,login_user,login_requiredfrom flask_principal import Identity, AnonymousIdentity, identity_changed, current_app,IdentityContext#上下文处理,可以在jinja2判断是否有执行权限@auth.app_context_processordef context():    admin= IdentityContext(admin_permission)    return dict(admin=admin)#登录@auth.route('/login',methods=['GET','POST'])def login():    if request.method == 'POST':        username = request.form['username']        password = request.form['password']        check = request.form.get('check')        user = User.query.filter_by(username=username).first()        if user and user.check_password_hash(password):            if check:                login_user(user,remember=True)            else:                login_user(user)            #登录时的身份变化            identity_changed.send(                current_app._get_current_object(),                identity=Identity(user.id))            return redirect(url_for('main.index'))        else:            flash("账户或密码错误!")    return render_template('login.html')#注销@auth.route('/logout',methods=['GET','POST'])def logout():    logout_user()    #注销后身份变成匿名    identity_changed.send(        current_app._get_current_object(),        identity=AnonymousIdentity())    return redirect(url_for('auth.login'))

需要权限保护的路由

#增加用户@auth.route('/adduser',methods=['GET','POST'])@login_required@admin_permission.require(http_exception=403)def adduser():    if request.method == 'POST':        username = request.form['user']        password = request.form['password']        search = User.query.filter_by(username=username).first()        if search is None:            user = User(username=username,password=password)            db.session.add(user)            db.session.commit()            flash('应该是提交成功了吧。')        else:            flash('这个用户名应该是有人用了吧。。')    return render_template('adduser.html')

前端鉴权,有权限才显示

   {% if admin.can() %}   <li><a href="{{ url_for('auth.adduser') }}"><i class="fa fa-circle-o"></i>增加用户</a></li>   {% endif %}