简体   繁体   English

如何从一个域重定向到另一个域并在响应中设置 cookies 或标头?

[英]How to redirect from one domain to another and set cookies or headers in the response?

I am using FastAPI's RedirectResponse and trying to redirect the user from one application (domain) to another with some cookie set, but the cookie always gets deleted.我正在使用 FastAPI 的RedirectResponse并尝试将用户从一个应用程序(域)重定向到另一个设置了一些 cookie 的应用程序,但 cookie 总是被删除。 If I try to add some headers, all the headers that I add to the redirect_response are deleted as well.如果我尝试添加一些标头,我添加到redirect_response的所有标头也会被删除。

@router.post("/callback")
async def sso_callback(request: Request):
   jwt_token = generate_token(request)
   redirect_response = RedirectResponse(url="http://192.168.10.1/app/callback", 
                             status_code=303)
   redirect_response.set_cookie(key="accessToken", value=jwt_token, httponly=True)
   redirect_response.headers["Authorized"] = str(jwt_token)
   return redirect_response

How can I solve this?我该如何解决这个问题? Thanks in advance for the help.在此先感谢您的帮助。

As described in this answer you cannot redirect to another domain with custom headers set, no matter what language or framework you use.本答案中所述,无论您使用哪种语言或框架,都无法重定向到设置了自定义标头的另一个域。 A redirection in the HTTP protocol is basically just a header (ie, the Location header) associated with the response, and it doesn't allow for any headers to the target location to be added. HTTP 协议中的重定向基本上只是与响应关联的 header(即Location标头),它不允许添加任何标头到目标位置。 When you are adding the Authorized header (which should normally be called 'Authorization' instead) using redirect_response.headers in the code snippet you provided, you are basically setting that header for the response which is instructing the browser/client to redirect, not for the redirect itself.当您在您提供的代码片段中使用redirect_response.headers添加Authorized header (通常应称为'Authorization' )时,您基本上将 header 设置为指示浏览器/客户端重定向的响应,而不是重定向本身。 In other words, you are sending that header back to the client .换句话说,您将 header 发送回客户端

As for the HTTP cookies , the browser stores the cookies sent by the server with the response (using the Set-Cookie response headers), and later sends the cookies with requests made to the same server inside a Cookie HTTP header. As for the HTTP cookies , the browser stores the cookies sent by the server with the response (using the Set-Cookie response headers), and later sends the cookies with requests made to the same server inside a Cookie HTTP header. As per the documentation :根据文档

The Set-Cookie HTTP response header is used to send a cookie from the server to the user agent, so that the user agent can send it back to the server later. Set-Cookie HTTP 响应 header 用于将 cookie 从服务器发送到用户代理,以便用户代理稍后将其发送回服务器。 To send multiple cookies, multiple Set-Cookie headers should be sent in the same response.要发送多个 cookies,应在同一个响应中发送多个Set-Cookie标头。

Hence, if this was a redirection from one application (with sub-domain, for example, abc.example.test ) to another (with sub-domain, for example, xyz.example.com ) that both have the same (parent) domain (and cookies have been set for example.test domain), cookies would be succesfully shared between the two applications.因此,如果这是从一个应用程序(具有子域,例如abc.example.test )重定向到另一个(具有子域,例如xyz.example.com )两者都具有相同(父)的应用程序域(并且 cookies 已设置为example.test域),cookies 将在两个应用程序之间成功共享。 The browser will make a cookie available to the given domain including any sub-domains, no matter which protocol (HTTP/HTTPS) or port is used;无论使用哪种协议(HTTP/HTTPS)或端口,浏览器都会为给定域(包括任何子域)提供 cookie; you can though limit its availability using the domain , path , secure , and httpOnly flags (see here and here ).您可以使用domainpathsecurehttpOnly标志来限制它的可用性(参见此处此处)。

However, you cannot set cookies for a different domain .但是,不能为不同的域设置 cookies If this was permitted, it would present an enormous security flaw.如果允许这样做,它将带来巨大的安全漏洞。 Hence, since you are "trying to redirect the user from one application (domain) to another with some cookie set,..." , it wouldn't work, as the cookie will only be sent with requests made to the same domain .因此,由于您“试图通过一些 cookie 集将用户从一个应用程序(域)重定向到另一个应用程序,......” ,它不会起作用,因为 cookie 只会与对同一域的请求一起发送。

Solution解决方案

One way to solve this, as described here , is to have domain (application) A redirecting the user to domain (application) B, with the "access token" passed in the URL as a query parameter.如此处所述,解决问题的一种方法是让域(应用程序)A 将用户重定向到域(应用程序)B,并在 URL 中传递“访问令牌”作为查询参数。 Domain B would then read the token and set its own cookie, so that the browser will send that cookie with every subsequent request to domain B. Example is given below.然后域 B 将读取令牌并设置自己的 cookie,以便浏览器将该 cookie 与每个后续请求一起发送到域 B。示例如下。

Please note that you should consider using a secure (HTTPS) communication, so that the token is transfered encrypted.请注意,您应该考虑使用安全 (HTTPS) 通信,以便加密传输令牌。 Also, note that having the token in the query string poses a serious security risk , as sensitive data should never be passed in the query string.另外,请注意,在查询字符串中包含令牌会带来严重的安全风险,因为敏感数据永远不应在查询字符串中传递。 This is because the query string, which is part of the URL, appears in the address bar of the browser;这是因为作为 URL 一部分的查询字符串出现在浏览器的地址栏中; thus, allowing the user to see and bookmark the URL with the token in it (meaning that it is saved on the disk).因此,允许用户查看带有令牌的 URL 并为其添加书签(意味着它保存在磁盘上)。 Also, the URL will make it to the browsing history , which means it will be written to the disk anyway and appear in the History tab (you can press Ctrl+H to access the browser's history).此外,URL 将进入浏览历史记录,这意味着它将被写入磁盘并出现在History选项卡中(您可以按Ctrl+H访问浏览器的历史记录)。 Both the above would allow attackers (as well as people you share the computer/mobile device/etc. with) to steal such sensitive information.上述两种情况都将允许攻击者(以及与您共享计算机/移动设备/等的人)窃取此类敏感信息。 Additionally, many browser plugins/extensions track users' browsing activity—every URL you visit is sent to their servers for analysis, in order to detect malicious websites and provide you with a warning when visiting such sites.此外,许多浏览器插件/扩展程序会跟踪用户的浏览活动——您访问的每个 URL 都会发送到他们的服务器进行分析,以便检测恶意网站并在访问此类网站时向您发出警告。 Hence, you should take all the above into account when using the approach below (for related posts on this subject, see here , here and here ).因此,在使用以下方法时,您应该考虑以上所有因素(有关此主题的相关帖子,请参见此处此处此处)。

To prevent displaying the URL in the address bar, the approach below uses a redirection on domain B as well.为了防止在地址栏中显示 URL,以下方法也在域 B 上使用重定向。 Once domain B receives the request for /submit route with the token in the query parameters of the URL, domain B responds with a redirection to a bare URL with no tokens (ie, its home page).一旦域 B 在 URL 的查询参数中收到带有令牌的/submit路由请求,域 B 就会响应重定向到没有令牌的裸 URL(即其home )。 Because of this redirection, the URL with the token in it wouldn't end up in the browsing history.由于这种重定向,带有令牌的 URL 不会出现在浏览历史记录中。 Although this provides some kind of protection against certain attacks described earlier, it doesn't mean that browser extensions/tools/etc.尽管这提供了针对前面描述的某些攻击的某种保护,但这并不意味着浏览器扩展/工具/等。 won't still be able to capture the URL with the token in it.仍然无法捕获带有令牌的 URL。

If you are testing this on localhost, you need to give application B a different domain name;如果你在 localhost 上测试这个,你需要给应用 B 一个不同的域名; otherwise, as mentioned earlier, cookies will be shared between applications having the same domain, and hence, you would end up receiving the cookies set for application A as well, and couldn't tell if the approach is working at all.否则,如前所述,cookies 将在具有相同域的应用程序之间共享,因此,您最终也会收到为应用程序 A 设置的 cookies,并且无法判断该方法是否有效。 To do that, you have to edit the /etc/hosts file (on Windows this is located in C:\Windows\System32\drivers\etc ) and assign a domain name to 127.0.0.1 .为此,您必须编辑/etc/hosts文件(在 Windows 上,该文件位于C:\Windows\System32\drivers\etc )并将域名分配给127.0.0.1 For example:例如:

127.0.0.1 example.test

You shouldn't add the scheme or port to the domain, as well as shouldn't use common extensions, such as .com , .net , etc., otherwise it may conflict with accessing other websites on the internet.请勿将方案或端口添加到域中,也不应使用常见的扩展名,例如.com.net等,否则可能会与访问 Internet 上的其他网站发生冲突。

In app A below, you need to access the home route (ie, / ) in your browser, where you can find and click the submit button to perform a POST request to the /submit route to start the redirection.在下面的应用 A 中,您需要在浏览器中访问主路由(即/ ),您可以在其中找到并单击submit按钮,对/submit路由执行POST请求以开始重定向。 The only reason for the POST request is because you are using it in your example and I am assuming you have to post some form-data . POST请求的唯一原因是因为您在示例中使用了它,并且我假设您必须发布一些form-data Otherwise, you could use a GET request as well.否则,您也可以使用GET请求。 In app B, when performing a RedirectResponse from a POST route (ie, /submit ) to a GET route (ie, / ), the response status code has to change to status.HTTP_303_SEE_OTHER , as described here , here and here , and as shown below.在应用程序 B 中,当执行从POST路由(即/submit )到GET路由(即/ )的RedirectResponse时,响应状态代码必须更改为status.HTTP_303_SEE_OTHER ,如此此处此处所述,以及如下图所示。 App A is listening on port 8000 , while app B is listening on port 8001 (adjust the ports as desired).应用程序 A 正在侦听端口8000 ,而应用程序 B 正在侦听端口8001 (根据需要调整端口)。

appA.py应用程序A.py

from fastapi import FastAPI
from fastapi.responses import RedirectResponse, HTMLResponse
import uvicorn

app = FastAPI()

@app.get('/', response_class=HTMLResponse)
def home():
    html_content = """
    <!DOCTYPE html>
    <html>
       <body>
          <h2>Click the "submit" button to be redirected to domain B</h2>
          <form method="POST" action="/submit">
             <input type="submit" value="Submit">
          </form>
       </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)
        
@app.post("/submit")
def submit():
    token = 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3'
    redirect_url = f'http://example.test:8001/submit?token={token}'  
    return RedirectResponse(redirect_url)
 
if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8000)

appB.py应用程序B.py

from fastapi import FastAPI, Request, status
from fastapi.responses import RedirectResponse
import uvicorn

app = FastAPI()

@app.get('/')
def home(request: Request):
    token = request.cookies.get('access-token')
    print(token)
    return 'You have been successfully redirected to domain B!' \
           f' Your access token ends with: {token[-4:]}'
 
@app.post("/submit")
def submit(request: Request, token: str):
    redirect_url = request.url_for('home')
    response = RedirectResponse(redirect_url, status_code=status.HTTP_303_SEE_OTHER)
    response.set_cookie(key="access-token", value=token, httponly=True)
    return response
 
if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=8001)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM