feat: 注册流程强制图形验证码
SSR 注册页与 /api/register 共用 CaptchaService,防刷同时保留换一张无 JS 刷新。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -237,11 +237,17 @@ func (h *Handlers) APIRegister(c *gin.Context) {
|
||||
Nickname string `json:"nickname" form:"nickname"`
|
||||
Email string `json:"email" form:"email" binding:"required"`
|
||||
EmailCode string `json:"email_code" form:"email_code"`
|
||||
CaptchaID string `json:"captcha_id" form:"captcha_id"`
|
||||
Captcha string `json:"captcha" form:"captcha"`
|
||||
}
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
if h.Captcha == nil || !h.Captcha.Verify(req.CaptchaID, req.Captcha) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "验证码错误或已过期"})
|
||||
return
|
||||
}
|
||||
|
||||
userCount := h.Auth.UserCount()
|
||||
mailReady := h.Settings.MailReady()
|
||||
|
||||
@@ -107,6 +107,7 @@ func Setup(cfg *config.Config) (*gin.Engine, error) {
|
||||
Mail: mailSvc,
|
||||
SitePage: sitePageSvc,
|
||||
Report: reportSvc,
|
||||
Captcha: captchaSvc,
|
||||
}, authMW)
|
||||
|
||||
r.GET("/media/thumb/*filepath", h.ServeImageThumb)
|
||||
@@ -114,6 +115,9 @@ func Setup(cfg *config.Config) (*gin.Engine, error) {
|
||||
r.GET("/health", h.APIHealth)
|
||||
r.GET("/robots.txt", h.RobotsTxt)
|
||||
r.GET("/sitemap.xml", h.SitemapXML)
|
||||
// 机器注册辅助(与 SSR 注册共用 CaptchaService)
|
||||
r.GET("/api/captcha", h.APICaptcha)
|
||||
r.POST("/api/register", h.APIRegister)
|
||||
|
||||
// OIDC Provider(外部机器 / Gitea SSO)
|
||||
r.GET("/.well-known/openid-configuration", h.OIDCDiscovery)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -22,6 +23,8 @@ type registerData struct {
|
||||
Email string
|
||||
MailReady bool
|
||||
RequireEmailCode bool
|
||||
CaptchaID string
|
||||
CaptchaSVG template.HTML
|
||||
}
|
||||
|
||||
type registerForm struct {
|
||||
@@ -151,6 +154,12 @@ func (d Deps) RegisterPost(c *gin.Context) {
|
||||
d.renderRegister(ctx, "操作过于频繁,请稍后再试", form)
|
||||
return
|
||||
}
|
||||
captchaID := strings.TrimSpace(c.PostForm("captcha_id"))
|
||||
captchaAns := strings.TrimSpace(c.PostForm("captcha"))
|
||||
if d.Captcha == nil || !d.Captcha.Verify(captchaID, captchaAns) {
|
||||
d.renderRegister(ctx, "验证码错误或已过期", form)
|
||||
return
|
||||
}
|
||||
mailReady := d.Settings.MailReady()
|
||||
if mailReady {
|
||||
code := strings.TrimSpace(c.PostForm("email_code"))
|
||||
@@ -197,14 +206,21 @@ func (d Deps) renderRegister(ctx *webctx.Context, errMsg string, form registerFo
|
||||
func (d Deps) renderRegisterWithChrome(ctx *webctx.Context, chrome PageChrome, errMsg string, form registerForm) {
|
||||
chrome.Error = errMsg
|
||||
mailReady := d.Settings.MailReady()
|
||||
ctx.HTML(http.StatusOK, "auth/register", registerData{
|
||||
data := registerData{
|
||||
PageChrome: chrome,
|
||||
Username: form.Username,
|
||||
Nickname: form.Nickname,
|
||||
Email: form.Email,
|
||||
MailReady: mailReady,
|
||||
RequireEmailCode: mailReady,
|
||||
})
|
||||
}
|
||||
if d.Captcha != nil {
|
||||
if id, svg, err := d.Captcha.Generate(); err == nil {
|
||||
data.CaptchaID = id
|
||||
data.CaptchaSVG = template.HTML(svg)
|
||||
}
|
||||
}
|
||||
ctx.HTML(http.StatusOK, "auth/register", data)
|
||||
}
|
||||
|
||||
type forgotPasswordData struct {
|
||||
|
||||
@@ -28,6 +28,7 @@ type Deps struct {
|
||||
Mail *services.MailService
|
||||
SitePage *services.SitePageService
|
||||
Report *services.ReportService
|
||||
Captcha *services.CaptchaService
|
||||
}
|
||||
|
||||
// SitePageLink 导航/页脚站点单页链接
|
||||
|
||||
Reference in New Issue
Block a user