Python中Flask利用url_map进行简单权限管理的实现思路

最近在弄 flask 简单的权限管理,想到一种实现方式:

利用 url_map 把所有的连接路径导出

Map([<Rule '/topics/create' (GET, OPTIONS, HEAD, POST) -> topics.cre
 <Rule '/topics/latest' (GET, OPTIONS, HEAD) -> topics.latest>,
 <Rule '/users/setting' (GET, OPTIONS, HEAD, POST) -> users.setting>
 <Rule '/auth/signout' (GET, OPTIONS, HEAD) -> auth.signout>,
 <Rule '/auth/signin' (GET, OPTIONS, HEAD, POST) -> auth.signin>,
 <Rule '/auth/signup' (GET, OPTIONS, HEAD, POST) -> auth.signup>,
 <Rule '/auth/reset' (GET, OPTIONS, HEAD, POST) -> auth.reset>,
 <Rule '/auth/find' (GET, OPTIONS, HEAD, POST) -> auth.find>,
 <Rule '/topics/' (GET, OPTIONS, HEAD) -> topics.index>,
 <Rule '/' (GET, OPTIONS, HEAD) -> frontend.index>,
 <Rule '/topics/<id>/reply' (OPTIONS, POST) -> topics.reply>,
 <Rule '/topics/<id>' (GET, OPTIONS, HEAD) -> topics.topic>,
 <Rule '/static/<filename>' (GET, OPTIONS, HEAD) -> static>,
 <Rule '/users/<username>' (GET, OPTIONS, HEAD) -> users.index>])

然后在 flask 里面添加权限控制页面将每个路径授权给用户。 具体方式为:数据库用户表增加一列,把允许的内容存为字典或者 json 串。每次请求时候比对路径是否在字典里面如果在就允许访问。 扩展开来,类似传参和 get、post 不同的访问也可以很方便的判断。 具体 url_map 使用参考这里: https://segmentfault.com/q/1010000000698148

但是现在有个问题是当启用了 blueprint 后,url_map 居然无法直接显示,大家有啥办法没?


Python中Flask利用url_map进行简单权限管理的实现思路

2 回复

在Flask里用url_map做权限管理,核心思路就是利用app.url_map这个属性来获取所有已注册的路由规则,然后根据当前用户权限来过滤。下面是一个可以直接跑的示例:

from flask import Flask, request, abort, render_template_string
from functools import wraps

app = Flask(__name__)

# 模拟用户权限数据
USER_PERMISSIONS = {
    'user1': ['/admin', '/dashboard'],
    'user2': ['/dashboard', '/profile']
}

def permission_required(allowed_paths):
    """装饰器:检查用户是否有权限访问特定路径"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # 获取当前用户(这里简单用查询参数模拟)
            current_user = request.args.get('user', 'guest')
            
            # 获取当前请求的路径
            current_path = request.path
            
            # 检查路径是否在允许列表中
            if current_user in USER_PERMISSIONS:
                allowed = USER_PERMISSIONS[current_user]
                # 检查精确匹配或前缀匹配
                if any(current_path.startswith(path) for path in allowed):
                    return f(*args, **kwargs)
            
            abort(403)  # 没有权限
        return decorated_function
    return decorator

# 动态注册路由时应用权限控制
def register_protected_route(rule, endpoint, view_func, allowed_paths, **options):
    """注册带权限控制的路由"""
    wrapped_func = permission_required(allowed_paths)(view_func)
    app.add_url_rule(rule, endpoint, wrapped_func, **options)

# 视图函数
def admin_page():
    return "Admin Area"

def dashboard():
    return "Dashboard"

def profile():
    return "User Profile"

# 手动注册路由并指定权限路径
register_protected_route('/admin', 'admin', admin_page, ['/admin'])
register_protected_route('/dashboard', 'dashboard', dashboard, ['/dashboard'])
register_protected_route('/profile', 'profile', profile, ['/profile'])

# 公开路由
@app.route('/')
def index():
    return render_template_string('''
        <h1>测试链接:</h1>
        <a href="/admin?user=user1">user1访问/admin</a><br>
        <a href="/admin?user=user2">user2访问/admin</a><br>
        <a href="/dashboard?user=user1">user1访问/dashboard</a>
    ''')

if __name__ == '__main__':
    app.run(debug=True)

这个实现的关键点:

  1. app.url_map的使用:虽然示例中直接用了request.path,但在更复杂的场景下,你可以遍历app.url_map.iter_rules()来获取所有注册的路由,然后建立权限映射表。

  2. 装饰器模式:通过装饰器在请求进入视图函数前进行权限校验,保持代码干净。

  3. 路径匹配:使用startswith()进行前缀匹配,这样/admin可以匹配/admin/admin/users等子路径。

  4. 动态路由注册:通过register_protected_route函数封装路由注册逻辑,确保权限控制与路由绑定。

更高级的实现可以考虑:

  • 把权限数据存数据库
  • 支持正则表达式匹配
  • 集成到Flask的before_request钩子做全局控制
  • 用蓝图(Blueprint)来分组管理权限

不过对于简单需求,上面这个方案完全够用了。

总结:用url_map做权限控制就是路由和权限的映射管理。


url_map 倒不是关键问题,看一下源码或者搜索一下应该就能解决。主要是需要这么细粒度的权限管理吗,还要查数据库,而且耦合还很严重,加页面得更新数据库

回到顶部