Python 我该如何通过这条路;“下一步”;使用Flask和Flask登录的URL?

Python 我该如何通过这条路;“下一步”;使用Flask和Flask登录的URL?,python,forms,flask,flask-login,Python,Forms,Flask,Flask Login,有关处理“下一个”URL的文档。这个想法似乎是: 用户转到/secret 用户被重定向到登录页面(例如,/login) 成功登录后,用户将重定向回/secret 我发现的使用Flask登录的唯一半完整示例是。这很有帮助,但因为它不包含HTML模板文件,我正在考虑是否可以将它们重新创建为一个自我训练练习 下面是/secret和/login部分的简化版本: @app.route("/secret") @fresh_login_required def secret(): return ren

有关处理“下一个”URL的文档。这个想法似乎是:

  • 用户转到
    /secret
  • 用户被重定向到登录页面(例如,
    /login
  • 成功登录后,用户将重定向回
    /secret
  • 我发现的使用Flask登录的唯一半完整示例是。这很有帮助,但因为它不包含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")
    
    现在,当用户访问
    /secret
    时,他会被重定向到
    /login?next=%2Fsecret
    。到目前为止,还不错,“next”参数在查询字符串中

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

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

    有一种解决方案似乎有效-将
    标记从

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

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

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


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

    如果需要在表单中指定不同的操作属性,则不能使用Flask login提供的下一个参数。我建议将端点而不是url放在url参数中,因为这样更容易验证。下面是来自我正在开发的应用程序,也许这可以帮助你

    覆盖Flask登录的未经授权处理程序,以便在下一个参数中使用端点而不是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))
    
    在您自己的URL中也使用
    request.endpoint

    {#登录表单}
    ...
    
    重定向到下一个参数中的端点(如果该端点存在且有效),否则重定向到回退

    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")
    

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

    索引/

    然后改为使用(request.endpoint,**request.view_args)的url_,因为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")
    

    如果有人试图使用Flask Login通过“下一个”URL,但使用Flask_Restful,我一直使用的解决方法是将“下一个”参数从GET方法传递到POST方法。使用Flask_Restful,单击Login.html中的Login按钮后,“下一个页面”参数设置为“无”

    login.html

    。。。
    记得我吗
    登录
    ...
    
    auth.py

    类登录(资源):
    def get(自我):
    如果当前用户已通过身份验证:
    返回重定向(url_for('home'))
    标题={'Content-Type':'text/html'}
    #1-->#这里我将“下一页”传递到login.html
    返回make_响应(render_模板('login.html',next_page=request.args.get('next')),200,标题)
    def post(自我):
    #2-->#用户单击登录按钮后,我检索保存在GET方法中的下一个页面
    下一页=request.args.get('next')
    如果当前用户已通过身份验证:
    返回重定向(url_for('home'))
    #检查数据库中是否存在帐户
    现有账户=账户.objects(email=request.form.get('email')).first()
    #仅当URL是相对的时重定向,这确保了重定向
    #与应用程序位于同一站点中。
    如果现有账户:
    如果存在帐户,请检查密码(request.form.get('password'):
    登录\用户(现有\帐户,memory=request.form.get('memory\ me'))
    如果不是下一页或url解析(下一页)。netloc!='':
    返回重定向(url_for('home'))
    #3-->#这里我使用检索到的下一页参数
    返回重定向(url\u用于(下一页))
    #未确认的帐户
    flash('请检查您的登录详细信息并重试')
    标题={'Content-Type':'text/html'}
    #4-->#如果用户提供的数据错误,我也会在此处传递“下一页”
    返回make_响应(render_模板('login.html',next_page=next_page),200,标题)
    
    在上述所有答案中,我没有发现任何对我有用的东西。 但我的发现,我会和你分享

    login.html
    中,只需添加以下代码

    <input
        type="hidden"
        name="next"
        value="{{ request.args.get('next', '') }}"
    />
    
    这对我来说非常有效


    谢谢你…

    在我使用的表单中:next=request.args.get(“next”)。在redirect\u dest中,它可能应该是dest=request.args.get(“next”)@2ni在我的示例中,表单显示在每个页面的顶部,如果你想要一个单独的登录页面,你应该使用
    next=request.args.get(“next”)
    。修复了您的第二个问题,谢谢。如果您的URL可能包含变量,请使用
    request.path
    。这种方法完全没有自动验证@timakro的答案。在@timakro的答案中,
    next
    的值本质上仅限于注册的应用程序端点,因此恶意/第三方重定向是自动的我真的不认为你的自定义未经授权处理程序比Flask登录的默认行为有什么好处。@us
    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")
    
    <input
        type="hidden"
        name="next"
        value="{{ request.args.get('next', '') }}"
    />
    
    @app.route("/login", methods=["GET", "POST"])
    def login():
        if request.method == "POST":
            username = request.form.get("username")
            password = request.form.get("password")
            next_url = request.form.get("next")
    
            if username in users and users[username][1] == password:
                session["username"] = username
                if next_url:
                    return redirect(next_url)
                return redirect(url_for("profile"))
        return render_template("login.html")