Flask权限篇flask_principal

最近写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_manager
from flask_sqlalchemy import SQLAlchemy
from 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 "".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 "".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 Flask
from flask_sqlalchemy import SQLAlchemy
from 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 = "请登录后再进行访问该页面!"

#初始化Principal
principals = 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 auth
from .. import db,admin_permission
from ..models import User
from flask import redirect,render_template,request,url_for,flash
from flask_login import login_user,logout_user,current_user,login_required
# from flask_security import roles_required,current_user,logout_user,login_user,login_required
from flask_principal import Identity, AnonymousIdentity, identity_changed, current_app,IdentityContext



#上下文处理,可以在jinja2判断是否有执行权限
@auth.app_context_processor
def 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 %}

你可能感兴趣的:(Flask权限篇flask_principal)