在 Golang 应用程序中,我使用带有 mySQL 后端的 gorilla/session 在 session 中存储数据,但我想将数据存储在 chi 路由器上下文中。如何将身份验证 token 字符串或结构添加到上下文中?我看到很多示例解释如何读取/使用它们,但没有一个示例解释如何将数据插入登录函数的上下文中。
例如chi文档here有以下代码来检查对我有用的管理员用户,但它没有提供关于 auth 对象首先如何进入上下文的线索。还有另一个中间件函数描述了如何将文章结构加载到上下文中并从 URL 获取其 id 参数,但这对我不起作用。我真的不想使用 cookie,因为那样会破坏使用上下文的整个目的。
// AdminOnly middleware restricts access to just administrators.
func AdminOnly(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
isAdmin, ok := r.Context().Value("acl.admin").(bool)
if !ok || !isAdmin {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
在下面的示例中,我想在上下文中存储用户结构或身份验证 token 。
此外,我将如何处理上下文中的闪烁?我可以这样做还是应该坚持使用基于数据库的 Web 应用程序 session ?
理想情况下,我想对提供模板的 Web 应用程序和单独的 API 应用程序使用相同的方法。
这是我的 web 应用程序登录代码,经过一些简化以去除多余的部分:
func (rs *appResource) login(w http.ResponseWriter, r *http.Request) {
// appResource contains db connection, session store and templates
var authUser AuthUser //struct
session, err := rs.store.Get(r, "admin-data")
email := r.FormValue("email")
password := r.FormValue("password")
sqlStatement := "SELECT id, venue_id, first_name, last_name, email, password FROM Users WHERE email=?"
row := rs.db.QueryRow(sqlStatement, email)
err = row.Scan(&authUser.UserID, &authUser.VenueID, &authUser.FirstName, &authUser.LastName, &authUser.Email, &authUser.Password)
if err = bcrypt.CompareHashAndPassword([]byte(authUser.Password), []byte(password)); err != nil {
session.AddFlash("Your password is incorrect")
session.Save(r, w)
http.Redirect(w, r, "/signin", http.StatusFound)
return
}
authUser.Authenticated = true
session.Values["authUser"] = authUser
firstName := authUser.FirstName
message := fmt.Sprintf("Welcome, %s!", firstName)
session.AddFlash(message)
session.Save(r, w)
http.Redirect(w, r, "/", http.StatusFound)
}
最佳答案
从有关上下文的 go 文档中,要在上下文中设置一个值,您只需使用 WithValue .
给你一个想法,最简单的例子是:
package main
import (
"fmt"
"context"
)
func main() {
const key = "myKey"
ctx := context.Background()
ctx := context.WithValue(ctx, key, "someVal")
v, ok := ctx.Value(key).(string)
if !ok {
fmt.Println("key does not exist in the context")
return
}
fmt.Printf("found %v", v)
}
现在,按照示例,在上下文中设置新值非常容易。当我们谈论 r.Context()
时,来自它的上下文先前已使用 WithContext 在 *http.Request
请求中设置。 .举个简单的例子:
package main
import (
"http"
"context"
)
func main() {
req, err := http.NewRequest(
http.MethodGet,
"/someEdp",
nil,
)
if err != nil {
fmt.Println(err)
return
}
ctx := context.Background()
ctx := context.WithValue(ctx, "auth-token", "mySecretToken")
req = req.WithContext(ctx)
// Do the request
}
要在您的处理程序中读取它就像:
func (rs *appResource) login(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Context()
v, ok := ctx.Value("auth-token").(string)
if !ok {
fmt.Println("ups")
return
}
fmt.Printf("found %v", v)
// Do something
}
现在,如果您想使用这种机制在上下文中安全地存储和读取授权 token ,我建议您看一下这篇关于 context keys 的文章由@Mat Ryer 撰写。
基本上,任何“拦截”您的请求的人都可能从您的请求中读取授权 token ,因为您使用的是字符串作为 key 。
相反,您应该定义私有(private)上下文 key ,以允许您从上下文中设置/读取授权 token 。
package main
import (
"fmt"
"context"
)
type contextKey string
var contextKeyAuthtoken = contextKey("auth-token")
func setToken(ctx context.Context, token string) context.Context {
return context.WithValue(ctx, contextKeyAuthtoken, token)
}
func getToken(ctx context.Context) (string, bool) {
tokenStr, ok := ctx.Value(contextKeyAuthtoken).(string)
return tokenStr, ok
}
func main() {
ctx := context.Background()
ctx = setToken(ctx, "someToken")
t, ok := getToken(ctx)
if !ok {
fmt.Println("unauthorized")
return
}
fmt.Printf("authorized with token %v", t)
}
这是 playground我为上面的代码段设置。
因此,您将在请求的使用者(您的接收者处理程序)中使用 getToken
并在另一个服务(?)或您服务的另一个逻辑组件中使用 setToken
。
通过这种方式,只有您才能从上下文中读取您的授权 token ,并允许用户做某事或不做某事。
关于go - 如何通过登录将 session 数据存储在 chi 路由器上下文中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54802619/