Golang Goth会话丢失问题:认证后访问根路径无用户信息
问题:Goth包认证后根路径无法读取会话信息
我正在使用Goth包按照示例代码测试,认证功能正常,回调后能看到用户信息,但访问/路径时会话为空,无法查看用户信息。请问我遗漏了什么?
package main import ( "context" "fmt" "github.com/gorilla/sessions" "html/template" "log" "net/http" "os" "sort" "github.com/gorilla/pat" "github.com/joho/godotenv" "github.com/markbates/goth" "github.com/markbates/goth/gothic" "github.com/markbates/goth/providers/azuread" ) func main() { err := godotenv.Load() if err != nil { log.Fatal("Error loading file") } store := sessions.NewFilesystemStore("", []byte(os.Getenv("SESSION_KEY"))) store.Options.Path = "/" store.Options.HttpOnly = true store.Options.Secure = false store.MaxLength(6000) gothic.Store = store goth.UseProviders( azuread.New(os.Getenv("AZUREAD_KEY"), os.Getenv("AZUREAD_SECRET"), "http://localhost:8080/auth/azuread/callback", azuread.ProviderOpts{TenantID: os.Getenv("AZUREAD_TENANT_ID")}), ) m := map[string]string{ "azuread": "Azure AD", } var keys []string for k := range m { keys = append(keys, k) } sort.Strings(keys) providerIndex := &ProviderIndex{Providers: keys, ProvidersMap: m} p := pat.New() p.Get("/auth/{provider}/callback", func(res http.ResponseWriter, req *http.Request) { user, err := gothic.CompleteUserAuth(res, req) if err != nil { fmt.Fprintln(res, err) return } t, _ := template.New("foo").Parse(userTemplate) t.Execute(res, user) }) p.Get("/logout/{provider}", func(res http.ResponseWriter, req *http.Request) { gothic.Logout(res, req) res.Header().Set("Location", "/") res.WriteHeader(http.StatusTemporaryRedirect) }) p.Get("/auth/{provider}", func(res http.ResponseWriter, req *http.Request) { // try to get the user without re-authenticating if gothUser, err := gothic.CompleteUserAuth(res, req); err == nil { t, _ := template.New("foo").Parse(userTemplate) t.Execute(res, gothUser) } else { gothic.BeginAuthHandler(res, req) } }) p.Get("/", func(res http.ResponseWriter, req *http.Request) { req = req.WithContext(context.WithValue(req.Context(), gothic.ProviderParamKey, "azuread")) user, err := gothic.CompleteUserAuth(res, req) if err != nil { t, _ := template.New("foo").Parse(indexTemplate) t.Execute(res, providerIndex) } else { t, _ := template.New("foo").Parse(userTemplate) t.Execute(res, user) } }) log.Println("listening on localhost:8080") log.Fatal(http.ListenAndServe(":8080", p)) } type ProviderIndex struct { Providers []string ProvidersMap map[string]string } var indexTemplate = `{{range $key,$value:=.Providers}} <p><a href="/auth/{{$value}}">Log in with {{index $.ProvidersMap $value}}</a></p> {{end}}` var userTemplate = ` <p><a href="/logout/{{.Provider}}">logout</a></p> <p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p> <p>Email: {{.Email}}</p> <p>NickName: {{.NickName}}</p> <p>Location: {{.Location}}</p> <p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p> <p>Description: {{.Description}}</p> <p>UserID: {{.UserID}}</p> <p>AccessToken: {{.AccessToken}}</p> <p>ExpiresAt: {{.ExpiresAt}}</p> <p>RefreshToken: {{.RefreshToken}}</p> `
问题原因
gothic.CompleteUserAuth的设计逻辑是:仅在回调阶段调用一次,用于从会话中取出临时认证数据、交换为用户信息,之后会自动清除会话中的临时认证数据。你在根路径再次调用该方法时,会话已经没有可用的认证数据,自然返回错误。
解决方案
需要在回调阶段手动将用户信息存入会话,后续从自定义会话中读取用户数据,而非重复调用CompleteUserAuth。
修改后的关键代码
- 回调函数:保存用户信息到会话
p.Get("/auth/{provider}/callback", func(res http.ResponseWriter, req *http.Request) { user, err := gothic.CompleteUserAuth(res, req) if err != nil { fmt.Fprintln(res, err) return } // 将用户信息存入自定义会话 session, _ := store.Get(req, "goth-user-session") session.Values["current_user"] = user session.Save(req, res) // 重定向到根路径 res.Header().Set("Location", "/") res.WriteHeader(http.StatusTemporaryRedirect) })
- 根路径:从会话读取用户信息
p.Get("/", func(res http.ResponseWriter, req *http.Request) { session, err := store.Get(req, "goth-user-session") if err != nil || session.Values["current_user"] == nil { t, _ := template.New("foo").Parse(indexTemplate) t.Execute(res, providerIndex) } else { user := session.Values["current_user"].(goth.User) t, _ := template.New("foo").Parse(userTemplate) t.Execute(res, user) } })
- 登出函数:清空自定义会话
p.Get("/logout/{provider}", func(res http.ResponseWriter, req *http.Request) { gothic.Logout(res, req) // 清空自定义会话中的用户信息 session, _ := store.Get(req, "goth-user-session") delete(session.Values, "current_user") session.Save(req, res) res.Header().Set("Location", "/") res.WriteHeader(http.StatusTemporaryRedirect) })
内容的提问来源于stack exchange,提问作者trookam
相关产品推荐
相关产品推荐

