2

我正在开发一个 Flask 应用程序,它允许用户使用 OAuth(以 Github 作为提供者)和 flask-dance 库登录。由于某种原因,在成功登录后,我无法重定向到我将用户发送到登录页面的页面。

当用户尝试连接到例如http://localhost:6675/examples/tutorial.first/时,用户将被重定向到登录页面,在 URL 中显示我们应该重定向到的页面 ( http://localhost :6675/login?next=%2Fexamples%2Ftutorial.first%2F )

问题是,在我设法使用 Github 登录后,应用程序只是回到主页。

我正在检查 Flask-dance文档,该make_github_blueprint()函数的文档提到了参数redirect_toredirect_url,但是当我尝试使用它们时,我什至无法完成登录步骤。此外,它似乎只适用于静态地址,而理想情况下我想跳回到登录前的页面。我也检查了这个 SO question,但那里的问题似乎有所不同。

有没有关于在使用 Flask dance 登录后如何正确进行重定向的示例?

这里有一些可能相关的代码片段。在初始化.py 文件中:

bp_github = make_github_blueprint(
    client_id="...",
    client_secret="...",
)
login_manager = LoginManager()
login_manager.login_github_view = 'github.login'
login_manager.login_view = 'login'

在 app.py 文件中:

@app.route("/login", methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return flask.redirect(flask.url_for('/'))
    return flask.render_template('login.html')

@app.route("/logout")
@login_required
def logout():
    logout_user()
    flask.flash("You have logged out")
    return flask.redirect(flask.url_for("login"))

@oauth_authorized.connect_via(bp_github)
def logged_in(blueprint, token):
    """
    create/login local user on successful OAuth login with github
    :param blueprint:
    :param token:
    :return:
    """
    if not token:
        flask.flash("Failed to log in.", category="error")
        return False

    session = blueprint.session

    resp = session.get("/user")

    if not resp.ok:
        msg = "Failed to fetch user info."
        flask.flash(msg, category="error")
        return False

    user_id = str(info["id"])

    # Find this OAuth token in the database, or create it
    query = OAuth.query.filter_by(
        provider=blueprint.name,
        provider_user_id=user_id,
    )
    try:
        oauth = query.one()
    except NoResultFound:
        oauth = OAuth(
            provider=blueprint.name,
            provider_user_id=user_id,
            token=token,
        )

    if oauth.user:
        login_user(oauth.user)
        flask.flash("Successfully signed in.")

    else:
        # Create a new local user account for this user
        name = info['login']

        user = User(
            email=info["email"],
            name=name,
            provider=provider
        )
        # Associate the new local user account with the OAuth token
        oauth.user = user
        # Save and commit our database models
        db.session.add_all([user, oauth])
        db.session.commit()
        # Log in the new local user account
        login_user(user)
        flask.flash("Successfully signed in.")

    # Disable Flask-Dance's default behavior for saving the OAuth token
    return False
4

4 回答 4

3

我遇到了同样的问题,我认为通过将下一个 url 作为参数传递,你试图完成的事情实际上是不可能的。如果您想使用“下一个”请求参数,您需要进入开发人员控制台并添加用户可能拥有的每个可能的重定向 url(这对您来说并不是一个很好的解决方案)。如果您真的想使其动态化,我建议您在 state 参数中传递下一个 url(有关 state 对象的更多详细信息,请参阅this。根据此文档:

您可以将此参数用于多种目的,例如将用户引导至应用程序中的正确资源、发送 nonce 和减少跨站点请求伪造。

不幸的是,Flask-Dance 不支持开箱即用,因此您需要在库中挖掘一点。我建议查看OAuth2ConsumerBlueprint对象并尝试实现适合您的自定义蓝图。

就我而言,我想在 OAuth 身份验证成功后将用户重定向到我域上的另一个应用程序。我对 Flask-Dance 所做的就是像这样在“next”参数中传递另一个应用程序 url

http://api.mycompany.com/auth/google?next=https://myotherapp.mycompany.com

我确保在谷歌控制台中添加完整的授权网址,如下所示:

http://api.mycompany.com/auth/google/authorized?next=https%3A%2F%2Fmyotherapp.mycompany.com

使用这种技术,您可以重定向到所需的 url,而无需在 flask-dance 中使用 redirect_to 参数(只能取一个值)

于 2018-08-31T14:52:58.127 回答
3

我的回答可能有点晚,因为您提到您的公司决定只允许用户在您的域上注册。但是,我仍在为可能有此问题的其他用户发布此信息。

为了解决这个问题,您需要有一个基本的登录页面,该页面首先存储通过域登录或 OAuth 登录完成登录后用户应重定向到的 next_url。当您在 URL 上使用 @login_required 时, Flask 在 URL 中添加next参数,这应该只允许登录用户使用。您需要添加session['next_url'] = request.args.get('next')登录路由以捕获next参数并将其存储在会话中作为next_url. 现在,在 OAuth 登录的情况下,一旦用户redirect_url在成功登录后被重定向到默认值,您将从会话中读取next_url值并将用户重定向到该 URL(如果存在)或呈现默认页面(通常是主页.)

我假设 2 件事,1. OAuth 登录成功后您的默认 URL 是/home. 在您的情况下,这是使用redirect_urlmake_provider_blueprint 函数设置的make_github_blueprint。其次,我假设您的登录页面是用户未登录时的登录页面,它显示启动 OAuth 登录过程的 GitHub 登录按钮。您可能需要稍微修改此流程以使您的案例next_url相应地捕获。

我在下面分享了处理这种情况所需的最少代码。请注意,我next_url在重定向之前从会话中弹出值。这样一来,如果用户在登录后尝试访问主页,他们不应该总是被重定向到 next_url(如果它存在于会话中)。

bp_github = make_github_blueprint(
    client_id="...",
    client_secret="...",
    redirect_url="/home"
)

@app.route("/login", methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return flask.redirect(flask.url_for('/'))
    session['next_url'] = request.args.get('next')
    return flask.render_template('login.html')

@auth.route("/")
@auth.route("/home")
@login_required
def home():
    if session.get('next_url'):
        next_url = session.get('next_url')
        session.pop('next_url', None)
        return redirect(next_url)
    return render_template('home.html')

让我知道这是否有帮助或您需要任何其他信息。我很乐意提供帮助。

于 2018-11-23T07:27:45.977 回答
1

我知道这是一个老问题,但我想谈谈我如何通过烧瓶舞和 oauth2 重定向克服这个问题。

我建议您像这样将下一页存储在会话变量中。

下面是用于身份验证的烧瓶舞蹈路线,您可以在此处获取引用页面并将其存储到会话变量中。

@main.route("/googleLogin")
def googleLogin():
    # here you store the page the user has come from with the referrer value
    session["next"] = request.referrer  


    # then do your login stuff
    if not google.authorized:
        return redirect(url_for("google.login")):
    else:
        return redirect(request.referrer, code=302)  #redirect if needed

@main.route("/yourAuthorisedLoginRoute") # probably / or /home
def authed_route():
    #you can add this section of code to 1) grab the next url if 
    # its there. If it is then destroy it so the user 
    #isn't redirect there next time they navigate to 
    #this page. or 2) skip if the next value doesn't exist.

    redirecter = session.get("next",None)
    if not redirecter is None:
        session.pop("next")
        return redirect(redirecter, code=302)

    #HERE IS YOUR NORMAL PAGE CODE.
    #i.e 
    return render_template("home.html")
于 2020-11-25T20:06:47.657 回答
1

我最近遇到了类似的问题,遇到了这个问题,并认为我会发布我的解决方案,以便其他人受益。其他答案中有很多很好的解释,说明需要做什么才能克服这个问题,所以我不会重复细节。解决方案背后的主要思想是相同的,即保存您当前的路线,然后在成功授权后重定向回它。

我的示例中的授权服务器是我公司的内部服务器,但它应该很容易替换OAuth2ConsumerBlueprint为您选择的提供商。

from flask import Flask, redirect, url_for, session, redirect, request
from flask_dance.consumer import OAuth2ConsumerBlueprint, oauth_authorized
from functools import wraps

app = Flask(__name__)

oauth_blueprint = OAuth2ConsumerBlueprint(
    "oauth",
    __name__,
    client_id="W7n...JD6",
    client_secret="QoU...pXEX",
    base_url='https://the.oauth.server.com',
    token_url='https://the.oauth.server.com/token/',
    authorization_url='https://the.oauth.server.com/authorize/',
    scope="openid profile",
)
app.register_blueprint(oauth_blueprint, url_prefix="/login")


def auth_required(func):
    @wraps(func)
    def check_authorization(*args, **kwargs):
        """check if authorized or token expired and authenticate if necessary"""

        token = oauth_blueprint.session.token
        if not oauth_blueprint.session.authorized or token["expires_in"] < 0:
            # store current route before redirecting so we can return after successful auth
            session["next_url"] = request.path
            return redirect(url_for("oauth.login"))
        return func(*args, **kwargs)

    return check_authorization


@oauth_authorized.connect
def redirect_to_next_url(blueprint, token):
    """
    allows the the callback url to be dynamic without having to register
    each protected endpoint as a redirect with OAuth2ConsumerBlueprint.
    """
    blueprint.token = token
    next_url = session["next_url"]
    return redirect(next_url)


@app.route('/my_protected_resource')
@auth_required
def my_protected_resource():
    token = oauth_blueprint.session.token

    # add your endpoint's logic here ...
    # decode token['id_token'] for user information
    # use token['access_token'] to access protected resources
    return
于 2021-03-22T19:39:32.140 回答