提交 7c07b623 编写于 作者: H HFO4

Feat: 2-FA login verification

上级 11e45bc7
......@@ -172,6 +172,13 @@ func GetActiveUserByID(ID interface{}) (User, error) {
return user, result.Error
}
// GetActiveUserByOpenID 用OpenID获取可登录用户
func GetActiveUserByOpenID(openid string) (User, error) {
var user User
result := DB.Set("gorm:auto_preload", true).Where("status = ? and open_id = ?", Active, openid).Find(&user)
return user, result.Error
}
// GetUserByEmail 用Email获取用户
func GetUserByEmail(email string) (User, error) {
var user User
......
......@@ -25,6 +25,7 @@ type User struct {
CreatedAt int64 `json:"created_at"`
PreferredTheme string `json:"preferred_theme"`
Score int `json:"score"`
Anonymous bool `json:"anonymous"`
Policy policy `json:"policy"`
Group group `json:"group"`
Tags []tag `json:"tags"`
......@@ -97,6 +98,7 @@ func BuildUser(user model.User) User {
CreatedAt: user.CreatedAt.Unix(),
PreferredTheme: user.OptionsSerialized.PreferredTheme,
Score: user.Score,
Anonymous: user.IsAnonymous(),
Policy: policy{
SaveType: user.Policy.Type,
MaxSize: fmt.Sprintf("%.2fmb", float64(user.Policy.MaxSize)/(1024*1024)),
......
......@@ -5,6 +5,7 @@ import (
"fmt"
model "github.com/HFO4/cloudreve/models"
"github.com/HFO4/cloudreve/pkg/authn"
"github.com/HFO4/cloudreve/pkg/qq"
"github.com/HFO4/cloudreve/pkg/serializer"
"github.com/HFO4/cloudreve/pkg/thumb"
"github.com/HFO4/cloudreve/pkg/util"
......@@ -126,6 +127,34 @@ func UserLogin(c *gin.Context) {
} else {
c.JSON(200, ErrorResponse(err))
}
}
// User2FALogin 用户二步验证登录
func User2FALogin(c *gin.Context) {
var service user.Enable2FA
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Login(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UserQQLogin 初始化QQ登录
func UserQQLogin(c *gin.Context) {
// 新建绑定
res, err := qq.NewLoginRequest()
if err != nil {
c.JSON(200, serializer.Err(serializer.CodeNotSet, "无法使用QQ登录", err))
return
}
// 设定QQ登录会话Secret
util.SetSession(c, map[string]interface{}{"qq_login_secret": res.SecretKey})
c.JSON(200, serializer.Response{
Data: res.URL,
})
}
......
......@@ -103,6 +103,10 @@ func InitMasterRouter() *gin.Engine {
{
// 用户登录
user.POST("session", controllers.UserLogin)
// 用户登录
user.POST("2fa", controllers.User2FALogin)
// 初始化QQ登录
user.POST("qq", controllers.UserQQLogin)
// WebAuthn登陆初始化
user.GET("authn/:username",
middleware.IsFunctionEnabled("authn_enabled"),
......
......@@ -6,6 +6,7 @@ import (
"github.com/HFO4/cloudreve/pkg/util"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
"github.com/pquerna/otp/totp"
)
// UserLoginService 管理用户登录的服务
......@@ -16,6 +17,32 @@ type UserLoginService struct {
CaptchaCode string `form:"captchaCode" json:"captchaCode"`
}
// Login 二步验证继续登录
func (service *Enable2FA) Login(c *gin.Context) serializer.Response {
if uid, ok := util.GetSession(c, "2fa_user_id").(uint); ok {
// 查找用户
expectedUser, err := model.GetActiveUserByID(uid)
if err != nil {
return serializer.Err(serializer.CodeNotFound, "用户不存在", nil)
}
// 验证二步验证代码
if !totp.Validate(service.Code, expectedUser.TwoFactor) {
return serializer.ParamErr("验证代码不正确", nil)
}
//登陆成功,清空并设置session
util.DeleteSession(c, "2fa_user_id")
util.SetSession(c, map[string]interface{}{
"user_id": expectedUser.ID,
})
return serializer.BuildUserResponse(expectedUser)
}
return serializer.Err(serializer.CodeNotFound, "登录会话不存在", nil)
}
// Login 用户登录函数
func (service *UserLoginService) Login(c *gin.Context) serializer.Response {
isCaptchaRequired := model.GetSettingByName("login_captcha")
......@@ -44,7 +71,11 @@ func (service *UserLoginService) Login(c *gin.Context) serializer.Response {
}
if expectedUser.TwoFactor != "" {
//TODO 二步验证处理
// 需要二步验证
util.SetSession(c, map[string]interface{}{
"2fa_user_id": expectedUser.ID,
})
return serializer.Response{Code: 203}
}
//登陆成功,清空并设置session
......
......@@ -16,11 +16,14 @@ type QQCallbackService struct {
// Callback 处理QQ互联回调
func (service *QQCallbackService) Callback(c *gin.Context, user *model.User) serializer.Response {
state := util.GetSession(c, "qq_login_secret")
if stateStr, ok := state.(string); !ok || stateStr != service.State {
return serializer.Err(serializer.CodeSignExpired, "请求过期,请重试", nil)
}
util.DeleteSession(c, "qq_login_secret")
// 获取OpenID
credential, err := qq.Callback(service.Code)
if err != nil {
return serializer.Err(serializer.CodeNotSet, "无法获取登录状态", err)
......@@ -28,6 +31,7 @@ func (service *QQCallbackService) Callback(c *gin.Context, user *model.User) ser
// 如果已登录,则绑定已有用户
if user != nil {
if user.OpenID != "" {
return serializer.Err(serializer.CodeCallbackError, "您已绑定了QQ账号,请先解除绑定", nil)
}
......@@ -37,6 +41,21 @@ func (service *QQCallbackService) Callback(c *gin.Context, user *model.User) ser
return serializer.Response{
Data: "/setting",
}
}
// 未登录,尝试查找用户
if expectedUser, err := model.GetActiveUserByOpenID(credential.OpenID); err == nil {
// 用户绑定了此QQ,设定为登录状态
util.SetSession(c, map[string]interface{}{
"user_id": expectedUser.ID,
})
res := serializer.BuildUserResponse(expectedUser)
res.Code = 203
return res
} else {
// 无匹配用户,创建新用户
}
return serializer.Response{}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册