http.Redirect() with headers

huangapple go评论86阅读模式
英文:

http.Redirect() with headers

问题

我有一个模拟授权API访问的网页。用户输入一个API URL和一个授权密钥(假设为"true"或"false")。在Go端,有一个处理该路径的处理函数,它读取表单并根据授权密钥生成一个令牌。

理想情况下,我希望将令牌保存为标头,并根据输入的API URL将请求重定向到正确的处理程序。然而,当我使用http.Redirect()时,我的标头不会作为请求的一部分发送。

func createTokenHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    path := r.FormValue("path")
    auth := r.FormValue("auth") // 假设为"true"或"false"
    w.Header().Set("auth", auth)
    http.Redirect(w, r, path, http.StatusFound) // 这是正确的HTTP状态码吗?
}

// path 重定向到此处理程序
func otherPathHandler(w http.ResponseWriter, r *http.Request) {
    // 这些对于使跨域请求与自定义标头正常工作是必需的,保留它们
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "auth")
    auth := r.Header.Get("auth")
    if auth == "true" {
        w.Write([]byte("已验证,这是API响应。"))
        // 继续处理
    }
}

可能会有多个具有不同路径的API URL,但我的思路是每次在同一个处理函数中将令牌写入标头。我这样设计是因为API URL最终可能会重定向到不同的域,所以我希望将其与读取表单分开。

谷歌告诉我要以某种方式使用Client.CheckRedirect,但我不确定如何在我的处理函数中实现它。如何在保留标头的同时进行重定向?谢谢!

英文:

I have a web page that emulates authorizing API access. The user enters an API url and an authorization key (let's say "true" or "false"). On the Go side, there's a handler function for this path which reads the form and generates a token based on the authorization key.
Ideally, I'd like to save the token as a header and redirect the request to the proper handler based on the API url entered. However, when I use http.Redirect(), my header does not get sent as part of the request.

func createTokenHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    path := r.FormValue("path")
    auth := r.FormValue("auth") // let's say "true" or "false"
    w.Header().Set("auth", auth)
    http.Redirect(w, r, path, http.StatusFound) // is this the correct http code?
}

// path redirects to this handler
func otherPathHandler(w http.ResponseWriter, r *http.Request) {
    // these were necessary for cross-domain requests to work with custom header, keeping them
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "auth")
    auth = r.Header.Get("auth")
    if auth == "true" {
    	w.Write([]byte("Validated, here's the API response."))
	    // continue
    }
}

There may be several API urls with different paths, but my thought process has the token being written as a header in that same handler function every time. I'm designing it this way because the API url may eventually redirect to different domains, so I'd like to keep it separate from reading the form.
Google tells me to use Client.CheckRedirect somehow, but I'm not sure how to implement it in my handler functions. How can I redirect while keeping my headers? Thank you!

答案1

得分: 13

这是因为HTTP标准在302/301重定向指令中不传递HTTP头部。这不是GoLang的问题,而是HTTP 1.0(和2.0?)标准的问题。

你的想法是正确的,对于常见的表单提交到你的Web应用程序,使用POST-REDIRECT-GET模式可以防止旧的浏览器刷新时出现“是否要重新提交表单”的错误提示。

顺便说一下,是的,那是正确的状态码。请注意,http.StatusFound是一个302状态码,表示临时移动。永远不要在Postback重定向中使用301永久移动状态。

然而,有一些常见的解决方法。

会话(Sessions)

几种服务器端语言都有会话(Sessions),可以在后端存储值。

  • 将值/结果存储在与用户会话相关联的后端数据库变量中
  • 重定向
  • 在新页面加载时读取会话值

ASP.NET就是这样一种后端语言。ASP.NET会话通过以下方式将会话与用户关联起来:1)在首次访问服务器时创建会话,2)获取会话ID并设置为浏览器会话(只在浏览器会话有效期内有效,关闭浏览器后cookie将被删除)。这样,在每次向服务器发出“请求”时,都会读取cookie中的会话ID,从数据存储中获取会话,并填充Session用户变量。

就个人而言,我从未在Go中复制这个功能。但是,你可以这样做。

请注意,这是确保“秘密”令牌的唯一方法,如果它是一个秘密的话。然而,有人可能窃取session_id并冒充请求。不过,你可以实现一个“四路浏览器指纹”来帮助限制冒充。不过,如果有人能够嗅探到包含session_id的cookie,那么他们将能够嗅探到指纹方法的所有参数(除了IP!)。

我个人最喜欢的方法是将值存储在一个临时浏览器会话cookie中,过期时间为Now() -1(即立即过期)。这样,你可以在重定向的Web请求中仅读取一次cookie,然后在该请求之后浏览器会将其过期。

这是我从ASP.NET MVC中学到的一个技巧,并在我所有的控制器上强制执行(通过禁用会话并编写自己的临时cookie处理程序)。

只需注意它不会是私密/保密的:它将通过你的连接发送。即使是HTTPS,它仍然会在浏览器的插件、cookie处理程序等中可见。是的,你可以对值进行“加密”,然后在服务器端进行解密以提高安全性:但是,它的安全性只取决于你的加密方式(并且会增加请求/响应的大小)。

传统的查询字符串参数

无法击败:

/my/url?token=23462345345345

由于你试图将其放入头部,所以它本来就不是一个秘密。

英文:

It's because the HTTP standard does not pass through HTTP headers on an 302/301 directive. It's not a GoLang issue, it's an HTTP 1.0 (and 2.0?) standards issue.

You are thinking correctly, using the POST-REDIRECT-GET pattern for common FORM postings to your web app (it prevents the old browser-refresh "Do you want to resubmit the form?" annoying error).

And btw, yes that's the correct status code. Note that http.StatusFound is a 302 code, which indicates it is a temp move. Never use the 301 Permanent Move status for a Postback redirect.

There are some common work-arounds though.

Sessions

Several server-side languages have Sessions that store the value on the backend.

  • store the value/result in a variable tied to the user's session on the backend DB
  • redirect
  • read the session value on the new page load

ASP.NET is one such backend language. ASP.NET sessions ties a session to a user by 1) creating a session upon the first hit to the server and 2) taking the session_id and setting a brower-session (only valid while the browser session is active, close the browser and the cookie is removed). That way on every "request" to the server, the cookie is read for the sessions_id, it is grabbed from your data store, and populates the Session user var.

Personally, I've never duplicated this functionality in Go. But, one could.

Note, this really the only way to complete secure your "secret" token, if it is a secret. Then again, one could steal the session_id and impersonate the request. Then again, one could implement a 4-way browser fingerprint to help limit impersonation. Then again, if one is able to sniff the cookie that has the session_id, then they would be able to sniff all the parameters of the fingerprint method (except IP!).

My personal favorite is storing the value in a tmp browser-session cookie with an expiration of Now() -1 (meaning, it will expire immediately). This allows you to read the cookie only 1 time, on the redirect's web request, before the browser expires it after that request.

It's a trick I picked up from ASP.NET MVC, and I enforced on all of my controllers (by disabling Sessions and writing my own temp cookie handler).

Just note that it will not be private/secret: it will sent over your connection. Even if HTTPS, it will still be seen in the browser's add-ons, cookie handlers, etc. Yes, you could "encrypt" the value,a nd decrypt it back on the server side for security: but then, it's only as strong as your encryption (and bloats your request/response sizes).

Good 'ol Querystring Parameter

Can't beat:

/my/url?token=23462345345345

Since you were trying to put it into the header, it isn't a secret anyways.

huangapple
  • 本文由 发表于 2016年4月1日 07:43:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/36345696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定