邮箱验证码重置密码(吊销 session);后台可配 SMTP 并发送测试信。 Co-authored-by: Cursor <cursoragent@cursor.com>
307 lines
8.3 KiB
Go
307 lines
8.3 KiB
Go
package web
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"git.iioio.com/freefire/jiang13-forum/modules/webctx"
|
|
"git.iioio.com/freefire/jiang13-forum/services"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type loginData struct {
|
|
PageChrome
|
|
Username string
|
|
Redirect string
|
|
}
|
|
|
|
type registerData struct {
|
|
PageChrome
|
|
Username string
|
|
Nickname string
|
|
Email string
|
|
MailReady bool
|
|
RequireEmailCode bool
|
|
}
|
|
|
|
type registerForm struct {
|
|
Username string
|
|
Nickname string
|
|
Email string
|
|
}
|
|
|
|
// LoginGet 登录页
|
|
func (d Deps) LoginGet(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
chrome := d.chrome(ctx, "登录 · "+d.Settings.SiteBranding().Name, "", "")
|
|
if c.Query("banned") == "1" {
|
|
chrome.Error = "账号已被禁言"
|
|
}
|
|
ctx.HTML(http.StatusOK, "auth/login", loginData{
|
|
PageChrome: chrome,
|
|
Redirect: c.Query("redirect"),
|
|
})
|
|
}
|
|
|
|
// LoginPost 登录提交
|
|
func (d Deps) LoginPost(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if !ctx.CheckCSRF() {
|
|
d.renderLogin(ctx, "无效请求,请重试", c.PostForm("username"), c.PostForm("redirect"))
|
|
return
|
|
}
|
|
if d.Limiter != nil && !d.Limiter.Allow("login", c.ClientIP()) {
|
|
d.renderLogin(ctx, "操作过于频繁,请稍后再试", c.PostForm("username"), c.PostForm("redirect"))
|
|
return
|
|
}
|
|
user := strings.TrimSpace(c.PostForm("username"))
|
|
pass := c.PostForm("password")
|
|
redir := strings.TrimSpace(c.PostForm("redirect"))
|
|
if redir == "" || !strings.HasPrefix(redir, "/") || strings.HasPrefix(redir, "//") {
|
|
redir = "/"
|
|
}
|
|
token, _, err := d.Auth.Login(user, pass, c.ClientIP(), c.Request.UserAgent())
|
|
if err != nil {
|
|
msg := "用户名或密码错误"
|
|
if err == services.ErrUserBanned {
|
|
msg = "账号已被禁言"
|
|
}
|
|
d.renderLogin(ctx, msg, user, redir)
|
|
return
|
|
}
|
|
ctx.SetLoginCookie(token)
|
|
ctx.Redirect(redir)
|
|
}
|
|
|
|
func (d Deps) renderLogin(ctx *webctx.Context, errMsg, username, redir string) {
|
|
chrome := d.chrome(ctx, "登录 · "+d.Settings.SiteBranding().Name, "", "")
|
|
chrome.Error = errMsg
|
|
ctx.HTML(http.StatusOK, "auth/login", loginData{
|
|
PageChrome: chrome,
|
|
Username: username,
|
|
Redirect: redir,
|
|
})
|
|
}
|
|
|
|
// LogoutPost 退出
|
|
func (d Deps) LogoutPost(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.CheckCSRF() {
|
|
ctx.ClearLoginCookie()
|
|
}
|
|
ctx.Redirect("/")
|
|
}
|
|
|
|
// RegisterGet 注册页
|
|
func (d Deps) RegisterGet(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
d.renderRegister(ctx, "", registerForm{})
|
|
}
|
|
|
|
// RegisterSendCode POST 发送注册邮箱验证码
|
|
func (d Deps) RegisterSendCode(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
form := registerFormFrom(c)
|
|
if !ctx.CheckCSRF() {
|
|
d.renderRegister(ctx, "无效请求,请重试", form)
|
|
return
|
|
}
|
|
if d.Limiter != nil && !d.Limiter.Allow("register", c.ClientIP()) {
|
|
d.renderRegister(ctx, "操作过于频繁,请稍后再试", form)
|
|
return
|
|
}
|
|
if d.EmailCode == nil || !d.Settings.MailReady() {
|
|
d.renderRegister(ctx, "邮件服务未配置,无需验证码即可注册", form)
|
|
return
|
|
}
|
|
if err := d.EmailCode.SendRegisterCode(form.Email); err != nil {
|
|
d.renderRegister(ctx, err.Error(), form)
|
|
return
|
|
}
|
|
chrome := d.chrome(ctx, "注册 · "+d.Settings.SiteBranding().Name, "", "")
|
|
chrome.Flash = "验证码已发送,请查收邮箱"
|
|
d.renderRegisterWithChrome(ctx, chrome, "", form)
|
|
}
|
|
|
|
// RegisterPost 注册提交
|
|
func (d Deps) RegisterPost(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
form := registerFormFrom(c)
|
|
if !ctx.CheckCSRF() {
|
|
d.renderRegister(ctx, "无效请求,请重试", form)
|
|
return
|
|
}
|
|
if d.Limiter != nil && !d.Limiter.Allow("register", c.ClientIP()) {
|
|
d.renderRegister(ctx, "操作过于频繁,请稍后再试", form)
|
|
return
|
|
}
|
|
mailReady := d.Settings.MailReady()
|
|
if mailReady {
|
|
code := strings.TrimSpace(c.PostForm("email_code"))
|
|
if d.EmailCode == nil || !d.EmailCode.Verify(form.Email, code) {
|
|
d.renderRegister(ctx, services.ErrEmailCodeInvalid.Error(), form)
|
|
return
|
|
}
|
|
}
|
|
pass := c.PostForm("password")
|
|
pass2 := c.PostForm("password2")
|
|
if pass != pass2 {
|
|
d.renderRegister(ctx, "两次密码不一致", form)
|
|
return
|
|
}
|
|
user, err := d.Auth.Register(form.Username, pass, form.Nickname, form.Email)
|
|
if err != nil {
|
|
d.renderRegister(ctx, err.Error(), form)
|
|
return
|
|
}
|
|
sid, err := d.Auth.CreateSessionForUser(user, c.ClientIP(), c.Request.UserAgent())
|
|
if err != nil {
|
|
ctx.SetFlash("注册成功,请登录")
|
|
ctx.Redirect("/login")
|
|
return
|
|
}
|
|
ctx.SetLoginCookie(sid)
|
|
ctx.SetFlash("注册成功,欢迎加入")
|
|
ctx.Redirect("/")
|
|
}
|
|
|
|
func registerFormFrom(c *gin.Context) registerForm {
|
|
return registerForm{
|
|
Username: strings.TrimSpace(c.PostForm("username")),
|
|
Nickname: strings.TrimSpace(c.PostForm("nickname")),
|
|
Email: strings.TrimSpace(c.PostForm("email")),
|
|
}
|
|
}
|
|
|
|
func (d Deps) renderRegister(ctx *webctx.Context, errMsg string, form registerForm) {
|
|
chrome := d.chrome(ctx, "注册 · "+d.Settings.SiteBranding().Name, "", "")
|
|
d.renderRegisterWithChrome(ctx, chrome, errMsg, form)
|
|
}
|
|
|
|
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{
|
|
PageChrome: chrome,
|
|
Username: form.Username,
|
|
Nickname: form.Nickname,
|
|
Email: form.Email,
|
|
MailReady: mailReady,
|
|
RequireEmailCode: mailReady,
|
|
})
|
|
}
|
|
|
|
type forgotPasswordData struct {
|
|
PageChrome
|
|
Email string
|
|
MailReady bool
|
|
}
|
|
|
|
// ForgotPasswordGet 忘记密码页
|
|
func (d Deps) ForgotPasswordGet(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
d.renderForgotPassword(ctx, "", "")
|
|
}
|
|
|
|
// ForgotPasswordSendCode 发送重置验证码
|
|
func (d Deps) ForgotPasswordSendCode(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
email := strings.TrimSpace(c.PostForm("email"))
|
|
if !ctx.CheckCSRF() {
|
|
d.renderForgotPassword(ctx, "无效请求,请重试", email)
|
|
return
|
|
}
|
|
if d.Limiter != nil && !d.Limiter.Allow("login", c.ClientIP()) {
|
|
d.renderForgotPassword(ctx, "操作过于频繁,请稍后再试", email)
|
|
return
|
|
}
|
|
if d.EmailCode == nil || !d.Settings.MailReady() {
|
|
d.renderForgotPassword(ctx, services.ErrMailNotConfigured.Error(), email)
|
|
return
|
|
}
|
|
if err := d.EmailCode.SendResetCode(email); err != nil {
|
|
d.renderForgotPassword(ctx, err.Error(), email)
|
|
return
|
|
}
|
|
chrome := d.chrome(ctx, "忘记密码 · "+d.Settings.SiteBranding().Name, "", "")
|
|
chrome.Flash = "若该邮箱已注册,验证码将发送到邮箱"
|
|
d.renderForgotPasswordWithChrome(ctx, chrome, "", email)
|
|
}
|
|
|
|
// ForgotPasswordPost 提交重置
|
|
func (d Deps) ForgotPasswordPost(c *gin.Context) {
|
|
ctx := d.ctx(c)
|
|
if ctx.IsSigned() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
email := strings.TrimSpace(c.PostForm("email"))
|
|
if !ctx.CheckCSRF() {
|
|
d.renderForgotPassword(ctx, "无效请求,请重试", email)
|
|
return
|
|
}
|
|
if d.Limiter != nil && !d.Limiter.Allow("login", c.ClientIP()) {
|
|
d.renderForgotPassword(ctx, "操作过于频繁,请稍后再试", email)
|
|
return
|
|
}
|
|
if !d.Settings.MailReady() || d.EmailCode == nil {
|
|
d.renderForgotPassword(ctx, services.ErrMailNotConfigured.Error(), email)
|
|
return
|
|
}
|
|
code := strings.TrimSpace(c.PostForm("email_code"))
|
|
if !d.EmailCode.VerifyPurpose(services.EmailCodePurposeReset, email, code) {
|
|
d.renderForgotPassword(ctx, services.ErrEmailCodeInvalid.Error(), email)
|
|
return
|
|
}
|
|
pass := c.PostForm("new_password")
|
|
pass2 := c.PostForm("new_password2")
|
|
if pass != pass2 {
|
|
d.renderForgotPassword(ctx, "两次输入的新密码不一致", email)
|
|
return
|
|
}
|
|
if err := d.User.ResetPasswordByEmail(email, pass); err != nil {
|
|
d.renderForgotPassword(ctx, err.Error(), email)
|
|
return
|
|
}
|
|
ctx.SetFlash("密码已重置,请使用新密码登录")
|
|
ctx.Redirect("/login")
|
|
}
|
|
|
|
func (d Deps) renderForgotPassword(ctx *webctx.Context, errMsg, email string) {
|
|
chrome := d.chrome(ctx, "忘记密码 · "+d.Settings.SiteBranding().Name, "", "")
|
|
d.renderForgotPasswordWithChrome(ctx, chrome, errMsg, email)
|
|
}
|
|
|
|
func (d Deps) renderForgotPasswordWithChrome(ctx *webctx.Context, chrome PageChrome, errMsg, email string) {
|
|
chrome.Error = errMsg
|
|
ctx.HTML(http.StatusOK, "auth/forgot_password", forgotPasswordData{
|
|
PageChrome: chrome,
|
|
Email: email,
|
|
MailReady: d.Settings.MailReady(),
|
|
})
|
|
}
|