Go用户认证系统:如何强制用户设置博客标题后方可进入主页
问题描述
我正在用Go开发用户认证系统,流程是:引导用户填写邮箱、用户名、密码完成注册,随后向用户邮箱发送确认链接;用户点击确认链接后,必须为其博客选择标题,我需要确保用户未设置博客标题时无法进入主页。
我的ConfirmEmail函数代码如下:
func ConfirmEmail(w http.ResponseWriter, r *http.Request){ err := r.ParseForm() if err != nil{ log.Fatal("Unable to parse data") } token := r.Form.Get("token") db.ConnectDB() current_time := time.Now().Unix() user_id := 0 var date_generated int64 var date_expires int64 var date_used int64 row := db.Db.QueryRow("Select user_id, date_generated, date_expires, date_used from Token where token = ?", token) if err := row.Scan(&user_id, &date_generated, &date_expires, &date_used); err != nil{ if err == sql.ErrNoRows{ //todo: no such token provide a link to signup.. fmt.Println("No such rows..") } else { log.Fatal("Something went wrong:", err) } } //reuse of the token... if (date_used != 0){ http.Redirect(w,r, "/signup", http.StatusFound) } // use of expired token... if(date_expires < current_time){ //todo: inform about the expired token and prompt for re confirmation.. fmt.Println("Token expired..") } else{ //todo: Check for blog title, if null prompt. var title string var username string if err := db.Db.QueryRow("select username, blogTitle from User where user_id = ?", user_id).Scan(&username, &title); err != nil{ if err == sql.ErrNoRows{ http.Redirect(w, r, "/signup", http.StatusFound) } } //want to do this until title is not provided.. if len(title) == 0{ err = templates.ExecuteTemplate(w, "chose-title.html", struct { Username string Msg string }{ Username: username, Msg: "", }) if err != nil { log.Fatal("Unable to render provided template:",err) } return } _, err = db.Db.Exec("Update Token set date_used = ? where token=?",current_time, token) if err != nil { log.Fatal("Unable to update with given data") } _, err = db.Db.Exec("Update User set Verified = true where user_id=?",user_id) if err != nil { log.Fatal("Unable to update with given data") } else { http.Redirect(w, r, "/login", http.StatusFound) } } }
核心问题代码片段:
if len(title) == 0{ err = templates.ExecuteTemplate(w, "chose-title.html", struct { Username string Msg string }{ Username: username, Msg: "", }) if err != nil { log.Fatal("Unable to render provided template:",err) } return } _, err = db.Db.Exec("Update Token set date_used = ? where token=?",current_time, token) if err != nil { log.Fatal("Unable to update with given data") } _, err = db.Db.Exec("Update User set Verified = true where user_id=?",user_id) if err != nil { log.Fatal("Unable to update with given data") } else { http.Redirect(w, r, "/login", http.StatusFound) } }
我曾考虑使用while循环实现持续校验,但认为该方案不可行,请问有哪些其他可行的替代方案或工作流可以实现这一需求?
可行解决方案
方案1:拆分路由,单独处理博客标题设置
把博客标题设置的逻辑从ConfirmEmail中抽离,单独创建SetBlogTitle路由,将流程拆分为两步:
- 确认邮箱环节:完成token验证后,先标记token已使用、用户已验证,再检查用户是否有博客标题:
- 无标题:将用户ID存入会话(如
gorilla/sessions),重定向到/set-blog-title - 有标题:直接跳转至登录页或主页
- 无标题:将用户ID存入会话(如
- 标题设置环节:
/set-blog-title路由负责渲染设置页面、接收用户提交的标题:- 校验标题合法性(非空、符合长度要求)
- 合法则更新数据库中用户的
blogTitle字段,完成后跳转至登录/主页 - 不合法则重新渲染页面并提示错误
示例代码片段:
// 修改后的ConfirmEmail函数 func ConfirmEmail(w http.ResponseWriter, r *http.Request) { // ... 原有token验证、过期/重复使用检查逻辑 ... // 验证通过后,先更新token和用户验证状态 _, err = db.Db.Exec("Update Token set date_used = ? where token=?", current_time, token) if err != nil { log.Fatal("Failed to update token status") } _, err = db.Db.Exec("Update User set Verified = true where user_id=?", user_id) if err != nil { log.Fatal("Failed to update user verification status") } // 查询用户博客标题 var title string err = db.Db.QueryRow("select blogTitle from User where user_id = ?", user_id).Scan(&title) if err != nil && err != sql.ErrNoRows { log.Fatal("Failed to query user blog title") } // 无标题则跳转至设置页面 if len(strings.TrimSpace(title)) == 0 { session, _ := store.Get(r, "user-session") session.Values["user_id"] = user_id session.Save(r, w) http.Redirect(w, r, "/set-blog-title", http.StatusFound) return } // 有标题则跳转至登录页 http.Redirect(w, r, "/login", http.StatusFound) } // 新增SetBlogTitle处理函数 func SetBlogTitle(w http.ResponseWriter, r *http.Request) { // 从会话获取用户ID session, _ := store.Get(r, "user-session") userID, ok := session.Values["user_id"].(int) if !ok { http.Redirect(w, r, "/signup", http.StatusFound) return } if r.Method == http.MethodPost { r.ParseForm() title := strings.TrimSpace(r.Form.Get("blog_title")) // 标题为空则重新渲染页面并提示错误 if len(title) == 0 { username := getUsernameByID(userID) // 实现该函数从数据库查询用户名 err := templates.ExecuteTemplate(w, "chose-title.html", struct { Username string Msg string }{ Username: username, Msg: "博客标题不能为空,请重新输入", }) if err != nil { log.Fatal("Failed to render template") } return } // 更新数据库中的博客标题 _, err := db.Db.Exec("Update User set blogTitle = ? where user_id=?", title, userID) if err != nil { log.Fatal("Failed to update blog title") } // 标题设置完成,跳转至登录页 http.Redirect(w, r, "/login", http.StatusFound) return } // GET请求时渲染标题设置页面 username := getUsernameByID(userID) err := templates.ExecuteTemplate(w, "chose-title.html", struct { Username string Msg string }{ Username: username, Msg: "", }) if err != nil { log.Fatal("Failed to render template") } }
方案2:调整注册流程,提前设置博客标题
如果业务允许,可将博客标题设置环节前移至注册阶段:用户注册时需同时填写邮箱、用户名、密码和博客标题,确认邮箱后直接完成流程。此方案适合不需要“确认后再设置标题”的场景。
方案3:用中间件拦截未设置标题的请求
为防止用户通过其他路径绕过标题设置进入主页,可编写全局中间件,拦截所有需要验证标题状态的路由:
- 检查当前登录用户是否已设置博客标题
- 未设置则重定向至标题设置页面
- 已设置则继续处理请求
示例中间件代码:
func RequireBlogTitle(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 从会话获取当前登录用户ID session, _ := store.Get(r, "user-session") userID, ok := session.Values["user_id"].(int) if !ok { http.Redirect(w, r, "/login", http.StatusFound) return } // 查询用户博客标题 var title string err := db.Db.QueryRow("select blogTitle from User where user_id = ?", userID).Scan(&title) if err != nil || len(strings.TrimSpace(title)) == 0 { http.Redirect(w, r, "/set-blog-title", http.StatusFound) return } // 标题已设置,继续处理请求 next.ServeHTTP(w, r) }) }
将中间件应用到需要拦截的路由:
http.Handle("/home", RequireBlogTitle(http.HandlerFunc(HomeHandler))) http.Handle("/dashboard", RequireBlogTitle(http.HandlerFunc(DashboardHandler)))
内容的提问来源于stack exchange,提问作者IcanCode
相关产品推荐
相关产品推荐

