Feat: reset password by Email

pull/247/head
HFO4 4 years ago
parent 7027a96258
commit 1ac4767db7

@ -33,3 +33,17 @@ func NewActivationEmail(userName, activateURL string) (string, string) {
return fmt.Sprintf("【%s】注册激活", options["siteName"]),
util.Replace(replace, options["mail_activation_template"])
}
// NewResetEmail 新建重设密码邮件
func NewResetEmail(userName, resetURL string) (string, string) {
options := model.GetSettingByNames("siteName", "siteURL", "siteTitle", "mail_reset_pwd_template")
replace := map[string]string{
"{siteTitle}": options["siteName"],
"{userName}": userName,
"{resetUrl}": resetURL,
"{siteUrl}": options["siteURL"],
"{siteSecTitle}": options["siteTitle"],
}
return fmt.Sprintf("【%s】密码重置", options["siteName"]),
util.Replace(replace, options["mail_reset_pwd_template"])
}

@ -151,6 +151,28 @@ func User2FALogin(c *gin.Context) {
}
}
// UserSendReset 发送密码重设邮件
func UserSendReset(c *gin.Context) {
var service user.UserResetEmailService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Reset(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserReset 重设密码
func UserReset(c *gin.Context) {
var service user.UserResetService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Reset(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserActivate 用户激活
func UserActivate(c *gin.Context) {
var service user.SettingService

@ -104,9 +104,16 @@ func InitMasterRouter() *gin.Engine {
// 用户登录
user.POST("session", controllers.UserLogin)
// 用户注册
user.POST("", middleware.IsFunctionEnabled("register_enabled"), controllers.UserRegister)
user.POST("",
middleware.IsFunctionEnabled("register_enabled"),
controllers.UserRegister,
)
// 用二步验证户登录
user.POST("2fa", controllers.User2FALogin)
// 发送密码重设邮件
user.POST("reset", controllers.UserSendReset)
// 通过邮件里的链接重设密码
user.PATCH("reset", controllers.UserReset)
// 邮件激活
user.GET("activate/:id",
middleware.SignRequired(),
@ -118,11 +125,13 @@ func InitMasterRouter() *gin.Engine {
// WebAuthn登陆初始化
user.GET("authn/:username",
middleware.IsFunctionEnabled("authn_enabled"),
controllers.StartLoginAuthn)
controllers.StartLoginAuthn,
)
// WebAuthn登陆
user.POST("authn/finish/:username",
middleware.IsFunctionEnabled("authn_enabled"),
controllers.FinishLoginAuthn)
controllers.FinishLoginAuthn,
)
// 获取用户主页展示用分享
user.GET("profile/:id",
middleware.HashID(hashid.UserID),

@ -1,12 +1,18 @@
package user
import (
"fmt"
"github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/cache"
"github.com/HFO4/cloudreve/pkg/email"
"github.com/HFO4/cloudreve/pkg/hashid"
"github.com/HFO4/cloudreve/pkg/serializer"
"github.com/HFO4/cloudreve/pkg/util"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"github.com/pquerna/otp/totp"
"net/url"
"strings"
)
// UserLoginService 管理用户登录的服务
@ -17,6 +23,86 @@ type UserLoginService struct {
CaptchaCode string `form:"captchaCode" json:"captchaCode"`
}
// UserResetEmailService 发送密码重设邮件服务
type UserResetEmailService struct {
UserName string `form:"userName" json:"userName" binding:"required,email"`
CaptchaCode string `form:"captchaCode" json:"captchaCode"`
}
// UserResetService 密码重设服务
type UserResetService struct {
Password string `form:"Password" json:"Password" binding:"required,min=4,max=64"`
ID string `json:"id" binding:"required"`
Secret string `json:"secret" binding:"required"`
}
// Reset 重设密码
func (service *UserResetService) Reset(c *gin.Context) serializer.Response {
// 取得原始用户ID
uid, err := hashid.DecodeHashID(service.ID, hashid.UserID)
if err != nil {
return serializer.Err(serializer.CodeNotFound, "重设链接无效", err)
}
// 检查重设会话
resetSession, exist := cache.Get(fmt.Sprintf("user_reset_%d", uid))
if !exist || resetSession.(string) != service.Secret {
return serializer.Err(serializer.CodeNotFound, "链接已过期", err)
}
// 重设用户密码
user, err := model.GetActiveUserByID(uid)
if err != nil {
return serializer.Err(serializer.CodeNotFound, "用户不存在", err)
}
user.SetPassword(service.Password)
if err := user.Update(map[string]interface{}{"password": user.Password}); err != nil {
return serializer.DBErr("无法重设密码", err)
}
cache.Deletes([]string{fmt.Sprintf("%d", uid)}, "user_reset_")
return serializer.Response{}
}
// Reset 发送密码重设邮件
func (service *UserResetEmailService) Reset(c *gin.Context) serializer.Response {
// 检查验证码
isCaptchaRequired := model.IsTrueVal(model.GetSettingByName("forget_captcha"))
if isCaptchaRequired {
captchaID := util.GetSession(c, "captchaID")
util.DeleteSession(c, "captchaID")
if captchaID == nil || !base64Captcha.VerifyCaptcha(captchaID.(string), service.CaptchaCode) {
return serializer.ParamErr("验证码错误", nil)
}
}
// 查找用户
if user, err := model.GetUserByEmail(service.UserName); err == nil {
// 创建密码重设会话
secret := util.RandStringRunes(32)
cache.Set(fmt.Sprintf("user_reset_%d", user.ID), secret, 3600)
// 生成用户访问的重设链接
controller, _ := url.Parse("/reset")
finalURL := model.GetSiteURL().ResolveReference(controller)
queries := finalURL.Query()
queries.Add("id", hashid.HashID(user.ID, hashid.UserID))
queries.Add("sign", secret)
finalURL.RawQuery = queries.Encode()
// 发送密码重设邮件
title, body := email.NewResetEmail(user.Nick, strings.ReplaceAll(finalURL.String(), "/reset", "/#/reset"))
if err := email.Send(user.Email, title, body); err != nil {
return serializer.Err(serializer.CodeInternalSetting, "无法发送密码重设邮件", err)
}
}
return serializer.Response{}
}
// Login 二步验证继续登录
func (service *Enable2FA) Login(c *gin.Context) serializer.Response {
if uid, ok := util.GetSession(c, "2fa_user_id").(uint); ok {

Loading…
Cancel
Save