首页 文章

如何使用Flask和Flask-login传递“下一个”URL?

提问于
浏览
12

Flask-login的文档讨论了如何处理"next" URL . 这个想法似乎是:

  • 用户转到 /secret

  • 用户重定向到登录页面(例如 /login

  • 成功登录后,用户被重定向回 /secret

我发现使用Flask-login的唯一半完整示例是https://gist.github.com/bkdinoop/6698956 . 它包含HTML模板文件,我看到我是否可以将它们重新创建为自我训练练习 .

这是 /secret/login 部分的简化版本:

@app.route("/secret")
@fresh_login_required
def secret():
    return render_template("secret.html")

@app.route("/login", methods=["GET", "POST"])
def login():
    <...login-checking code omitted...>
    if user_is_logged_in:
        flash("Logged in!")
        return redirect(request.args.get("next") or url_for("index"))
    else:
        flash("Sorry, but you could not log in.")
        return render_template("login.html")

这里是 login.html

<form name="loginform" action="{{ url_for('login') }}" method="POST">
Username: <input type="text" name="username" size="30" />
Password: <input type="password" name="password" size="30" />
<input type="submit" value="Login" />

现在,当用户访问 /secret 时,他被重定向到 /login?next=%2Fsecret . 到目前为止,非常好 - "next"参数在查询字符串中 .

但是,当用户提交登录表单时,他将被重定向回索引页面,而不是返回到 /secret URL .

我猜测原因是因为传入URL上的“next”参数没有合并到登录表单中,因此在处理表单时不会作为变量传递 . 但是正确的方法是什么解决这个?

一个解决方案似乎有效 - 更改 <form> 标签

<form name="loginform" action="{{ url_for('login') }}" method="POST">

至:

<form name="loginform" method="POST">

删除"action"属性后,浏览器(至少Windows上的Firefox 45)会自动使用当前URL,从而继承 ?next=%2Fsecret 查询字符串,该字符串成功将其发送到表单处理处理程序 .

但是省略了“action”表单属性并让浏览器在正确的解决方案中填充它?它适用于所有浏览器和平台吗?

或者Flask或Flask-login是否打算以不同的方式处理?

2 回答

  • 14

    如果您需要在表单中指定不同的操作属性,则无论如何都可以建议将 endpoints 而不是url放入url参数,因为它更容易验证 . 在这里工作's some code from the application I',也许这可以帮到你 .

    覆盖Flask-Login的未授权处理程序,以使用 endpoints 而不是下一个参数中的url:

    @login_manager.unauthorized_handler
    def handle_needs_login():
        flash("You have to be logged in to access this page.")
        return redirect(url_for('account.login', next=request.endpoint))
    

    在您自己的网址中也使用 request.endpoint

    {# login form #}
    <form action="{{ url_for('account.login', next=request.endpoint) }}" method="post">
    ...
    </form>
    

    如果存在并且有效,则在下一个参数中重定向到 endpoints ,否则重定向到回退 .

    def redirect_dest(fallback):
        dest = request.args.get('next')
        try:
            dest_url = url_for(dest)
        except:
            return redirect(fallback)
        return redirect(dest_url)
    
    @app.route("/login", methods=["GET", "POST"])
    def login():
        ...
        if user_is_logged_in:
            flash("Logged in!")
            return redirect_dest(fallback=url_for('general.index'))
        else:
            flash("Sorry, but you could not log in.")
            return render_template("login.html")
    
  • 2

    @timakro提供了一个简洁的解决方案 . 如果要处理动态链接,例如

    index / <user>

    然后使用url_for(request.endpoint,** request.view_args),因为request.endpoint不包含动态可变信息:

    @login_manager.unauthorized_handler
     def handle_needs_login():
         flash("You have to be logged in to access this page.")
         #instead of using request.path to prevent Open Redirect Vulnerability 
         next=url_for(request.endpoint,**request.view_args)
         return redirect(url_for('account.login', next=next))
    

    以下代码应更改为:

    def redirect_dest(home):
        dest_url = request.args.get('next')
        if not dest_url:
            dest_url = url_for(home)
        return redirect(dest_url)
    
    @app.route("/login", methods=["GET", "POST"])
    def login():
        ...
        if user_is_logged_in:
            flash("Logged in!")
            return redirect_dest(home=anyViewFunctionYouWantToSendUser)
        else:
            flash("Sorry, but you could not log in.")
            return render_template("login.html")
    

相关问题