Golang API会话已运行,需实现路由鉴权与用户数据隔离
I'm trying to implement session functionality in my Golang API. I want to restrict access to certain endpoints so only logged-in users can access them, and ensure users can only retrieve their own exclusive data (preventing others from accessing non-self data).
I've already completed user registration (saving user info to a MongoDB collection) and login functionality (verifying that the user's input password matches the password hash in the user collection), and create a session for successfully logged-in users.
Now I'm testing session restriction on the default "/" endpoint, but have two questions:
- Can I create an auth middleware that I can just add to specific endpoints to avoid code duplication?
- When creating a session, I set
sess.Values["id"] = existingUser.ID.String()to query user-specific data. Is this approach secure?Here's the relevant code:
Top-level middleware configuration:
e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))Login logic:
err = bcrypt.CompareHashAndPassword([]byte(existingUser.HashedPassword), []byte(signInUser.Password)) if err != nil { fmt.Println(err) return c.String(http.StatusNotAcceptable, "Incorrect password") } // create the session sess, err := session.Get("session", c) if err != nil { fmt.Println(err) return err } sess.Options = &sessions.Options{ Path: "/", MaxAge: 86400 * 7, HttpOnly: true, } sess.Values["id"] = existingUser.ID.String() sess.Save(c.Request(), c.Response()) return c.String(http.StatusAccepted, "Signed In!")Default "/" route:
e.GET("/", func(c echo.Context) error { sess, err := session.Get("session", c) if err != nil { return c.JSON(http.StatusForbidden, "Access Denied") } if sess.Values["id"] == nil || sess.Values["id"] == "" { return c.JSON(http.StatusForbidden, "Access Denied 2") } return c.JSON(http.StatusOK, sess.Values["id"]) })
Great questions—let's tackle them one by one to get your session-based auth solid and secure.
1. Building a Reusable Auth Middleware
Absolutely! A middleware is exactly what you need to eliminate repetitive session-checking code across your endpoints. Here's how to implement one for your Echo API:
First, define the middleware function:
func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // Fetch the session from the request context sess, err := session.Get("session", c) if err != nil { return c.JSON(http.StatusForbidden, map[string]string{"error": "Access denied: invalid session"}) } // Validate the user ID exists in the session userID, isValid := sess.Values["id"].(string) if !isValid || userID == "" { return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Please log in to access this resource"}) } // Optional (but recommended): Verify the user still exists in MongoDB // This handles cases where an account is deleted but the session is still active // var user User // err = userCollection.FindOne(c.Request().Context(), bson.M{"_id": primitive.ObjectIDFromHex(userID)}).Decode(&user) // if err != nil { // return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Session invalid: user not found"}) // } // Pass the valid user ID to downstream handlers via context c.Set("userID", userID) // Let the request proceed to the protected endpoint return next(c) } }
Now apply this middleware to any protected endpoint (or groups of endpoints) instead of repeating the session check:
// Single protected endpoint e.GET("/", AuthMiddleware(func(c echo.Context) error { userID := c.Get("userID").(string) return c.JSON(http.StatusOK, map[string]string{"message": "Welcome!", "user_id": userID}) })) // Group of protected endpoints protectedAPI := e.Group("/api/v1") protectedAPI.Use(AuthMiddleware) protectedAPI.GET("/profile", func(c echo.Context) error { userID := c.Get("userID").(string) // Fetch user-specific profile data from MongoDB here return c.JSON(http.StatusOK, map[string]string{"profile_data": "Your exclusive info", "user_id": userID}) }) protectedAPI.POST("/settings", func(c echo.Context) error { userID := c.Get("userID").(string) // Update user settings for this specific user ID return c.JSON(http.StatusOK, map[string]string{"status": "settings updated", "user_id": userID}) })
2. Security of Storing User ID in Session
Your current approach is a strong foundation, but let's break down the security implications and key improvements:
What you're doing right:
- HttpOnly Cookie: Setting
HttpOnly: trueblocks client-side JavaScript from accessing the session cookie, which is critical for mitigating XSS attacks. - Session Expiry: A 7-day
MaxAgeensures sessions don't persist indefinitely, reducing the risk of misuse if a cookie is compromised.
Key improvements to harden security:
Use a Strong Secret Key: The
[]byte("secret")you're using for the cookie store is dangerously weak. Replace it with a long, random secret (at least 32 bytes). Generate one easily with:openssl rand -hex 32Store this secret in environment variables (never hard-code it) for production.
Enable Secure & SameSite Flags: For production (where you should use HTTPS), add these to your session options to prevent interception and CSRF attacks:
sess.Options = &sessions.Options{ Path: "/", MaxAge: 86400 * 7, HttpOnly: true, Secure: true, // Only send cookie over HTTPS SameSite: http.SameSiteStrictMode, // Block cross-site cookie usage }Validate User ID on Protected Endpoints: Even though the session cookie is signed (preventing tampering without your secret), it's wise to confirm the user ID corresponds to an existing account in MongoDB (like the optional step in the middleware). This handles edge cases where a user is deleted but their session is still active.
Avoid Sensitive Data: You're only storing the user ID, which is safe—never store passwords, hashed or otherwise, or personal data in session values.
Is storing the user ID safe?
Yes, as long as you follow the above best practices. The session cookie is signed with your secret key, so attackers can't modify the id value without knowing it. Combined with HttpOnly, Secure, and SameSite flags, this is a secure way to track authenticated users.
内容的提问来源于stack exchange,提问作者John

