Flask中验证Basic Auth用户名与URL用户名一致性的优化方案
这个问题我之前也遇到过,重复写校验代码确实繁琐,试试用自定义装饰器来解决,完美符合DRY(Don’t Repeat Yourself)原则!下面分两种场景给你具体实现方案:
场景1:使用Flask-Login进行认证
如果你的项目已经用了Flask-Login管理用户会话,可以基于它扩展一个带用户名匹配校验的装饰器:
from flask import Flask, abort from flask_login import LoginManager, UserMixin, current_user, login_required from functools import wraps app = Flask(__name__) app.secret_key = "your-secret-key-here" # 初始化Flask-Login login_manager = LoginManager() login_manager.init_app(app) # 模拟用户模型(实际项目替换为你的用户类) class User(UserMixin): def __init__(self, username): self.username = username self.id = username # 模拟加载用户的逻辑(实际从数据库查询) @login_manager.user_loader def load_user(user_id): return User(user_id) # 自定义装饰器:整合登录验证+URL用户名匹配 def validate_username_match(f): @wraps(f) @login_required # 先确保用户已登录 def decorated_function(*args, **kwargs): # 从路由参数中获取URL里的用户名 url_username = kwargs.get("name") # 对比URL用户名和当前认证用户的用户名 if not url_username or url_username != current_user.username: abort(403, description="你无权访问其他用户的资源") return f(*args, **kwargs) return decorated_function # 示例路由:无需再写重复校验 @app.route("/user/<name>/profile") @validate_username_match def user_profile(name): return f"欢迎访问你的个人主页,{name}!" @app.route("/user/<name>/settings") @validate_username_match def user_settings(name): return f"这里是你的设置页面,{name}!" # 自定义403错误页面 @app.errorhandler(403) def forbidden(error): return f"访问被拒绝:{error.description}", 403 if __name__ == "__main__": app.run(debug=True)
场景2:手动实现Basic Auth认证
如果是自己手写的Basic Auth逻辑,可以把认证和用户名校验整合到同一个装饰器里:
from flask import Flask, request, abort from functools import wraps app = Flask(__name__) # 模拟用户数据库(实际替换为你的用户校验逻辑) valid_users = { "alice": "alice123", "bob": "bob456" } # Basic Auth校验函数 def check_basic_auth(username, password): return username in valid_users and valid_users[username] == password # 返回认证要求的响应 def require_auth(): return ( "请使用合法凭证登录", 401, {"WWW-Authenticate": 'Basic realm="用户认证"'} ) # 自定义装饰器:整合Basic Auth+URL用户名匹配 def auth_and_validate_username(f): @wraps(f) def decorated_function(*args, **kwargs): # 先做Basic Auth认证 auth = request.authorization if not auth or not check_basic_auth(auth.username, auth.password): return require_auth() # 校验URL用户名和认证用户名是否一致 url_username = kwargs.get("name") if url_username != auth.username: abort(403, description="你无权操作其他用户的数据") return f(*args, **kwargs) return decorated_function # 示例路由 @app.route("/user/<name>/edit") @auth_and_validate_username def edit_user_profile(name): return f"正在编辑{name}的个人信息(当前认证用户:{request.authorization.username})" # 自定义403错误页面 @app.errorhandler(403) def forbidden(error): return f"访问被拒绝:{error.description}", 403 if __name__ == "__main__": app.run(debug=True)
额外优化:支持自定义路由参数名
如果你的路由里用户名参数不是name而是username,可以把装饰器改成可配置的:
def validate_username_match(param_name="name"): def decorator(f): @wraps(f) @login_required def decorated_function(*args, **kwargs): url_username = kwargs.get(param_name) if url_username != current_user.username: abort(403) return f(*args, **kwargs) return decorated_function return decorator # 使用时指定参数名 @app.route("/user/<username>/profile") @validate_username_match(param_name="username") def user_profile(username): # ...
这样所有需要校验的路由只需要加一行装饰器,再也不用重复写if name != u.username的判断了!
内容的提问来源于stack exchange,提问作者Jugbot
相关产品推荐
相关产品推荐

