Flask-Login 在用户登录后重定向到 React Client

Flask-Login Redirecting to React Client after User has been logged in

我正在尝试将 flask-login 与 React 应用程序集成。用户登录后,flask-login 似乎会强制执行页面重定向。

Since we don't want to redirect to the same page we have to make sure that the actual back redirect is slightly different (only use the submitted data, not the referrer)

来源: https://web.archive.org/web/20120504074405/http://flask.pocoo.org/snippets/62

文档显示的内容和我正在做的事情之间的区别是我的客户端在不同的域中(因此,我将面临 CORS 问题),而文档的客户端和服务器(我假设)在同一个域上。

这一点在这段代码中很明显:

def is_safe_url(target):
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url, target))
    return test_url.scheme in ('http', 'https') and \
           ref_url.netloc == test_url.netloc

从上面,函数 returns true if ref_url.netloc == test_url.netloc (为简单起见,我省略了其他部分)。我查看了 netloc 的属性,结果是没有顶级域的域名。例如,如果域名是:domain.com,那么 netloc 就是 domain.

但是,对于我的情况,这行不通。

例如, 前端:www.frontend.com 后端:www.backend.com

登录后端的请求应该重定向回 www.frontend.com/home。根据文档中提供的代码,它永远不会重定向到预期的目的地。所以我试着稍微修改一下代码,得到了这个:

def is_safe_url(request, target):
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url, target))
    return test_url.scheme in ['http', 'https'] and \
           (test_url.netloc == 'localhost:3000' or \
            ref_url.netloc == test_url.netloc 
           )

我想如果我以检查合适域的方式配置重定向(在我的示例中,它将是 localhost:3000)并检查不允许的域,那么不应该在这里受到任何安全威胁。但是,如果我遗漏了什么,请纠正我。

遗憾的是,这会导致 CORS 问题。

我还想补充一点,flask-login 提供的大多数示例都使用了 jinja 模板。文档中不是很清楚的一点是,如果您不使用重定向,是否应该使用它们?

其余代码:

def is_safe_url(request, target):
    ref_url = urlparse(request.host_url)
    test_url = urlparse(urljoin(request.host_url, target))
    return test_url.scheme in ['http', 'https'] and \
           (test_url.netloc == 'localhost:3000' or \
            ref_url.netloc == test_url.netloc 
           )

def get_redirect_target(request):
    for target in request.values.get('next'), request.referrer:
        print('target', target)
        if not target:
            continue
        if is_safe_url(request, target):
            return target

@user_bp.route('/login', methods=['GET', 'POST'])
def login():
    next = get_redirect_target(request)
    if current_user.is_authenticated:
        return jsonify({ 'login': True })
    if request.method == 'POST':
        data = request.get_json()
        form_input = ImmutableMultiDict(data)
        form = UserAuthenticationForm(form_input)        
        if form.validate():
            try:
                user = RegisteredUser.query.filter_by(email = data['user']).first() or RegisteredUser.query.filter_by(username = data['user']).first()
                if user is not None and user.check_password(data['password']):
                    login_user(user)
                    response = redirect(next)
                    print(response.get_data())
                    return response
                    # return jsonify({ 'login': True })
            except Exception as e: # Handle a faulty connection to the database
                print('Error: ' + str(e))
        return jsonify({ 'login': False })

编辑:

CORS 设置:

CORS(app, origins=["http://localhost:3000"], expose_headers=["Content-Type", "X-CSRFToken"], supports_credentials=True)

所以我认为您的第一个问题是您将 multi-page 网络应用程序流与单页网络应用程序流混合在一起。

在多页 Web 应用程序中,处理用户登录后,通常会将他们重定向到另一个页面,以便他们可以开始使用您的应用程序。但是在单页 Web 应用程序中,您通常不会重定向,而是发送一个令牌或客户端将持有的东西,以便在他们以后发出请求时对它们进行身份验证。对于 SPA,应用程序本身决定了用户如何使用该应用程序的流程。对于 MPA,服务器通过重定向或其他机制指示流量。

- 旁注,在 OAuth 等情况下使用重定向来对 SPA 进行身份验证。

该博客的代码还展示了如何进行安全重定向,但实际上它还做了其他事情。表单中的 next 输入是一种存储用户来自的页面的方式,以便在他们登录后您可以为他们提供更好的用户体验并将他们重定向回他们想要的页面。示例 我有一个我每天使用的页面的书签,但是我的登录 session 已过期。所以后端将 url 保存到 next 并将我重定向到登录页面并将 next 添加到页面,这样一旦我登录 next 就会被发送到服务器并且现在它可以将我重定向回书签页面。在 React 中,我们不需要这个,因为我可以将此信息存储在应用程序本身中,然后一旦我登录,我就可以通过 React 代码显示适当的页面。

现在您想要使用 flask-login,这很好。您需要做的是在您的 React 应用程序中向登录端点的后端发出 javascript post 请求。因此 backend.com/login 例如作为登录端点。登录成功后,您应该忽略默认的重定向行为并获取在 session header 中 returned 的 id。从现在开始,您可以在以后的所有请求中将该 ID 用作 session header,并且一切正常。

现在您可以使用 CORS。所以你有两个服务器。 frontend.combackend.comfrontend.com 为您的 React 应用提供服务,backend.com 是 api 和身份验证。

您需要在 backend.com 和白名单 frontend.com 上启用 CORS。这将告诉 backend.com 来自客户端的任何源自 frontend.com 的请求都可以,并且应该被处理。否则它应该 return CORS 错误。