15 -Flask构建弹幕微电影网站-基于角色的访问控制

本章内容: 基于角色的访问控制
项目源码地址:https://github.com/mtianyan/movie_project

基于角色的访问控制

角色的访问控制:

将职责和功能划分一个角色,比如电影管理员,预告管理员。

  1. 模型: Auth

  2. 表单: AuthForm

  3. 请求方法: GET POST

  4. 访问控制: @admin_login_req

首先定义authform

class AuthForm(FlaskForm):
    name = StringField(
        label="权限名称",
        validators=[
            DataRequired("权限名称不能为空!")
        ],
        description="权限名称",
        render_kw={            "class": "form-control",            "placeholder": "请输入权限名称!"
        }
    )
    url = StringField(
        label="权限地址",
        validators=[
            DataRequired("权限地址不能为空!")
        ],
        description="权限地址",
        render_kw={            "class": "form-control",            "placeholder": "请输入权限地址!"
        }
    )
    submit = SubmitField(        '编辑',
        render_kw={            "class": "btn btn-primary",
        }
    )

views中定义auth_add

权限添加

@admin.route("/auth/add/", methods=["GET", "POST"])@admin_login_reqdef auth_add():
    """
    添加权限
    """
    form = AuthForm()    if form.validate_on_submit():
        data = form.data
        auth = Auth(
            name=data["name"],
            url=data["url"]
        )
        db.session.add(auth)
        db.session.commit()
        flash("添加权限成功!", "ok")    return render_template("admin/auth_add.html",form=form)

将form传递到表单中,然后开始在模板中替换字段

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

记得自行加上表单令牌

为字段添加错误信息:

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加form表单的methods

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加flash消息闪现的显示

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加添加标签的权限

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

权限列表

views中

@admin.route("/auth/list/<int:page>/", methods=["GET"])@admin_login_reqdef auth_list(page=None):
    """
    权限列表
    """
    if page is None:
        page = 1
    page_data = Auth.query.order_by(
        Auth.addtime.desc()
    ).paginate(page=page, per_page=2)    return render_template("admin/auth_list.html", page_data=page_data)

取出所有的数据然后进行时间排序。

pagedata传递到了模板,进行字段填充,import pg 然后调用page方法。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

grid中为跳转list的url添加page参数。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

编辑与删除功能实现

@admin.route("/auth/del/<int:id>/", methods=["GET"])@admin_login_reqdef auth_del(id=None):
    """
    权限删除
    """
    auth = Auth.query.filter_by(id=id).first_or_404()
    db.session.delete(auth)
    db.session.commit()
    flash("删除权限成功!", "ok")    return redirect(url_for('admin.auth_list', page=1))@admin.route("/auth/edit/<int:id>/", methods=["GET", "POST"])@admin_login_reqdef auth_edit(id=None):
    """
    编辑权限
    """
    form = AuthForm()
    auth = Auth.query.get_or_404(id)    if form.validate_on_submit():
        data = form.data
        auth.url = data["url"]
        auth.name = data["name"]
        db.session.add(auth)
        db.session.commit()
        flash("修改权限成功!", "ok")
        redirect(url_for('admin.auth_edit', id=id))    return render_template("admin/auth_edit.html", form=form, auth=auth)

为编辑和删除按钮添加url

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

新建auth_edit.html复制auth_add的代码修改其中汉字部分以及填充值。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

权限列表中添加展示消息闪现的部分。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

角色管理

  1. 模型: Role

  2. 表单: RoleForm

  3. 请求方法: GET POST

  4. 访问控制: @admin_login_req

角色权限列表中存入id的字符串。

首先创建一个form Roleform

class RoleForm(FlaskForm):
    name = StringField(
        label="角色名称",
        validators=[
            DataRequired("角色名称不能为空!")
        ],
        description="角色名称",
        render_kw={            "class": "form-control",            "placeholder": "请输入角色名称!"
        }
    )    # 多选框
    auths = SelectMultipleField(
        label="权限列表",
        validators=[
            DataRequired("权限列表不能为空!")
        ],        # 动态数据填充选择栏:列表生成器
        coerce=int,
        choices=[(v.id, v.name) for v in Auth.query.all()],
        description="权限列表",
        render_kw={            "class": "form-control",
        }
    )
    submit = SubmitField(        '编辑',
        render_kw={            "class": "btn btn-primary",
        }
    )

在views中进行对于表单的校验以及传递到模板中。

@admin.route("/role/add/", methods=["GET", "POST"])@admin_login_reqdef role_add():
    """
    角色添加
    """
    form = RoleForm()    if form.validate_on_submit():
        data = form.data
        role = Role(
            name=data["name"],
            auths=",".join(map(lambda v: str(v), data["auths"]))
        )
        db.session.add(role)
        db.session.commit()
        flash("添加角色成功!", "ok")    return render_template("admin/role_add.html", form=form)

此时前往form中进行字段的填充。为模板中form添加post方法

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加提示的flash信息块。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加字段校验:

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

备用: 制作多选框checkbox 用widgets.ListWidget 包裹 widgets.CheckboxInput()自己做一个多选.然后choices=[(v.id, v.name) for v in Auth.query.all(). 这样传回来的也是[1,2,3]

角色列表的展示

@admin.route("/role/list/<int:page>/", methods=["GET"])@admin_login_reqdef role_list(page=None):
    """
    角色列表
    """
    if page is None:
        page = 1
    page_data = Role.query.order_by(
        Role.addtime.desc()
    ).paginate(page=page, per_page=2)    return render_template("admin/role_list.html", page_data=page_data)

views中定义角色列表,然后将pagedata进行填充,以及import pg与调用page方法进行分页。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

grid中的url添加page参数

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

因为会有删除功能添加flash信息显示

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

删除角色功能

@admin.route("/role/del/<int:id>/", methods=["GET"])@admin_login_reqdef role_del(id=None):
    """
    删除角色
    """
    role = Role.query.filter_by(id=id).first_or_404()
    db.session.delete(role)
    db.session.commit()
    flash("删除角色成功!", "ok")    return redirect(url_for('admin.role_list', page=1))

为list中的删除按钮添加对应的url

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

编辑角色功能实现

views中对于edit的处理

@admin.route("/role/edit/<int:id>/", methods=["GET", "POST"])@admin_login_reqdef role_edit(id=None):
    """
     编辑角色
    """
    form = RoleForm()
    role = Role.query.get_or_404(id)    # get时进行赋值。应对无法模板中赋初值
    if request.method == "GET":
        auths = role.auths
        form.auths.data = list(map(lambda v: int(v), auths.split(",")))    if form.validate_on_submit():
        data = form.data
        role.name = data["name"]
        role.auths = ",".join(map(lambda v: str(v), data["auths"]))
        db.session.add(role)
        db.session.commit()
        flash("修改角色成功!", "ok")    return render_template("admin/role_edit.html", form=form, role=role)

新建一个role_edit.html 把add中的内容粘贴过来,修改汉字。以及填充值

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

在list中修改指向编辑的链接。

管理员管理

  1. 模型: Admin

  2. 表单: AdminForm

  3. 请求方法: GET POST

  4. 访问控制: @admin_login_req

定义管理员的表单模型

class AdminForm(FlaskForm):
    name = StringField(
        label="管理员名称",
        validators=[
            DataRequired("管理员名称不能为空!")
        ],
        description="管理员名称",
        render_kw={            "class": "form-control",            "placeholder": "请输入管理员名称!",
        }
    )
    pwd = PasswordField(
        label="管理员密码",
        validators=[
            DataRequired("管理员密码不能为空!")
        ],
        description="管理员密码",
        render_kw={            "class": "form-control",            "placeholder": "请输入管理员密码!",
        }
    )
    repwd = PasswordField(
        label="管理员重复密码",
        validators=[
            DataRequired("管理员重复密码不能为空!"),
            EqualTo('pwd', message="两次密码不一致!")
        ],
        description="管理员重复密码",
        render_kw={            "class": "form-control",            "placeholder": "请输入管理员重复密码!",
        }
    )
    role_id = SelectField(
        label="所属角色",
        coerce=int,
        choices=[(v.id, v.name) for v in Role.query.all()],
        render_kw={            "class": "form-control",
        }
    )
    submit = SubmitField(        '编辑',
        render_kw={            "class": "btn btn-primary",
        }
    )

views中实例化form 并将form 传递到前台的模板中
将数据存入数据库

@admin.route("/admin/add/", methods=["GET", "POST"])@admin_login_reqdef admin_add():
    """
    添加管理员
    """
    form = AdminForm()    from werkzeug.security import generate_password_hash    if form.validate_on_submit():
        data = form.data
        admin = Admin(
            name=data["name"],
            pwd=generate_password_hash(data["pwd"]),
            role_id=data["role_id"],
            is_super=1
        )
        db.session.add(admin)
        db.session.commit()
        flash("添加管理员成功!", "ok")    return render_template("admin/admin_add.html", form=form)

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

添加flash消息显示和错误提示。(自行)注意为模板中form添加post方法。

管理员列表显示

views中添加管理员列表显示

@admin.route("/admin/list/<int:page>/", methods=["GET"])@admin_login_reqdef admin_list(page=None):
    """
    管理员列表
    """
    if page is None:
        page = 1
    page_data = Admin.query.join(
        Role
    ).filter(
        Role.id == Admin.role_id
    ).order_by(
        Admin.addtime.desc()
    ).paginate(page=page, per_page=1)    return render_template("admin/admin_list.html", page_data=page_data)

前往admin_list中填充pagedata 以及pg的import以及调用page方法进行分页。

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

grid中修改调转到list的url参数,添加上page=1

《15 -Flask构建弹幕微电影网站-基于角色的访问控制》

mark

访问权限控制

def admin_auth(f):    @wraps(f)
    def decorate_function(*args, **kwargs):
        # 权限查询
        abort(404)        return f(*args, **kwargs)    return decorate_function
# 调用权限装饰器@admin_auth

通过一个权限装饰器进行访问的控制,在装饰器里面进行一个查询,拿出session中保存的管理员的id,查询出对应的角色。同过角色查询出角色所对应的列表。列表是一个id的列表,id列表中去权限表中查询出我们能够访问的路由规则。根据路由规则进行匹配,得出我们能不能访问该页面。

print(request.script_root);print(request.url_rule)from flask import abort
def admin_auth(f):
    """
    权限控制装饰器
    """    @wraps(f)
    def decorated_function(*args, **kwargs):
        admin = Admin.query.join(
            Role
        ).filter(
            Role.id == Admin.role_id,
            Admin.id == session["admin_id"]
        ).first()
        auths = admin.role.auths        # 将原本存储的权限字符串转换为列表
        auths = list(map(lambda v: int(v), auths.split(",")))
        auth_list = Auth.query.all()
        urls = [v.url for v in auth_list for val in auths if val == v.id]
        rule = request.url_rule        if str(rule) not in urls:
            abort(404)        return f(*args, **kwargs)    return decorated_function

接下来为除过登录和后台首页都加上。然后测试是否可用。

登录我们的标签管理员。

进阶: 后台菜单动态显示,这个可以设计一个菜单数据表,分配可访问的菜单到用户权限中即可。

点赞