Go Web应用集成Keycloak OIDC遇重定向循环问题求助
问题:Keycloak OIDC认证中间件导致重定向循环
我正在开发大学项目的后端服务器,整体已基本完成。朋友帮我搭建了Keycloak实例,我已为服务器配置好客户端,Keycloak的RedirectURI设置为http://localhost:8080/callback。
我希望保护所有路由,要求用户必须登录才能访问,但尝试用中间件实现认证时,始终陷入重定向循环。ChatGPT给出的方案完全没用,特此求助。
原Router函数
func Network_Router() { r := mux.NewRouter() r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("public/assets")))) r.PathPrefix("/evaluation/data/").Handler(http.StripPrefix("/evaluation/data/", http.FileServer(http.Dir("../hugo/this/data")))) // Add Routing r.HandleFunc("/", Network_Homepage_Handler) r.HandleFunc("/createEvaluation", Network_CreateEvaluation_Handler) r.HandleFunc("/evaluation/{subdomain}", Network_EvaluationSite_Handler) r.HandleFunc("/api/evaldata", API_returnData) r.HandleFunc("/callback", CallbackHandler) http.ListenAndServe(":8080", r) }
导致重定向循环的完整代码
package main import ( "log" "net/http" "context" "fmt" "github.com/coreos/go-oidc" "github.com/gorilla/mux" _ "github.com/mattn/go-sqlite3" "golang.org/x/oauth2" "github.com/gorilla/sessions" ) var ( clientID = "data" clientSecret = "data" redirectURI = "http://localhost:8080/callback" keycloakIssuer = "data" store = sessions.NewCookieStore([]byte("test")) ) // 实际使用的是正确的配置数据 func main() { Network_Router() } func Network_Router() { r := mux.NewRouter() r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("public/assets")))) r.PathPrefix("/evaluation/data/").Handler(http.StripPrefix("/evaluation/data/", http.FileServer(http.Dir("../hugo/this/data")))) // Add Routing r.HandleFunc("/", AuthMiddleware(Network_Homepage_Handler)) r.HandleFunc("/createEvaluation", AuthMiddleware(Network_CreateEvaluation_Handler)) r.HandleFunc("/evaluation/{subdomain}", AuthMiddleware(Network_EvaluationSite_Handler)) r.HandleFunc("/api/evaldata", AuthMiddleware(API_returnData)) r.HandleFunc("/login", KeycloakLoginHandler) r.HandleFunc("/callback", CallbackHandler) http.ListenAndServe(":8080", r) } func CallbackHandler(w http.ResponseWriter, r *http.Request) { // Retrieve the session session, err := store.Get(r, "session") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Get the original requested URL from the session or query parameter originalURL, ok := session.Values["originalURL"].(string) if !ok { originalURL = "/" } // Clear the session value delete(session.Values, "originalURL") session.Save(r, w) state := r.URL.Query().Get("state") code := r.URL.Query().Get("code") log.Printf("CallbackHandler: state=%s, code=%s", state, code) // Perform token exchange and other necessary validation here // Redirect to the original requested location after successful authentication http.Redirect(w, r, originalURL, http.StatusSeeOther) } func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Retrieve the session session, err := store.Get(r, "session-name") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } idToken, err := validateToken(r) if err != nil { // User is not authenticated log.Printf("User not authenticated: %v", err) // Store the original requested URL in the session session.Values["originalURL"] = r.URL.Path session.Save(r, w) http.Redirect(w, r, "/login", http.StatusSeeOther) return } // User is authenticated, print some information for debugging log.Printf("Debug bug bug, %v", idToken) // Proceed to the next handler next.ServeHTTP(w, r) } } func validateToken(r *http.Request) (*oidc.IDToken, error) { ctx := context.Background() provider, err := oidc.NewProvider(ctx, keycloakIssuer) if err != nil { return nil, err } config := oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, RedirectURL: redirectURI, Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } // Exchange the authorization code for a token token, err := config.Exchange(ctx, r.URL.Query().Get("code")) if err != nil { return nil, err } // Verify the ID token rawIDToken, ok := token.Extra("id_token").(string) if !ok { return nil, fmt.Errorf("missing id_token in OAuth2 token response") } idToken, err := provider.Verifier(&oidc.Config{ClientID: clientID}).Verify(ctx, rawIDToken) if err != nil { return nil, err } log.Printf("validateToken: code=%s, clientID=%s, clientSecret=%s", r.URL.Query().Get("code"), clientID, clientSecret) return idToken, nil } func KeycloakLoginHandler(w http.ResponseWriter, r *http.Request) { // Redirect the user to the Keycloak login page ctx := context.Background() provider, err := oidc.NewProvider(ctx, keycloakIssuer) if err != nil { log.Printf("Error creating OIDC provider: %v", err) http.Error(w, "Unable to create OIDC provider", http.StatusInternalServerError) return } config := oauth2.Config{ ClientID: clientID, ClientSecret: clientSecret, RedirectURL: redirectURI, Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } state, err := randomString(10) if err != nil { log.Printf("Sucks to be you") } authCodeURL := config.AuthCodeURL(state) log.Printf("Redirecting to Keycloak login page: %s", authCodeURL) http.Redirect(w, r, authCodeURL, http.StatusSeeOther) }
注:这段代码大部分由ChatGPT生成,效果很差。除OIDC相关方法外,其他所有使用的方法均能正常工作。
内容的提问来源于stack exchange,提问作者panderu
相关产品推荐
相关产品推荐

