mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-03 20:40:26 +08:00
在管理员登录后才验证OTP
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
// 检查是否需要OTP
|
||||
type CheckOTPAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CheckOTPAction) RunPost(params struct {
|
||||
Username string
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
checkResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["requireOTP"] = checkResp.RequireOTP
|
||||
this.Success()
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
@@ -14,10 +13,8 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -27,7 +24,8 @@ type IndexAction struct {
|
||||
|
||||
// 首页(登录页)
|
||||
|
||||
var TokenSalt = stringutil.Rand(32)
|
||||
// TokenKey 加密用的密钥
|
||||
var TokenKey = stringutil.Rand(32)
|
||||
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
From string
|
||||
@@ -59,7 +57,7 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["menu"] = "signIn"
|
||||
|
||||
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
|
||||
this.Data["token"] = stringutil.Md5(TokenSalt+timestamp) + timestamp
|
||||
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
|
||||
this.Data["from"] = params.From
|
||||
|
||||
uiConfig, err := configloaders.LoadAdminUIConfig()
|
||||
@@ -93,9 +91,10 @@ func (this *IndexAction) RunPost(params struct {
|
||||
Password string
|
||||
OtpCode string
|
||||
Remember bool
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
CSRF *actionutils.CSRF
|
||||
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
CSRF *actionutils.CSRF
|
||||
}) {
|
||||
params.Must.
|
||||
Field("username", params.Username).
|
||||
@@ -112,7 +111,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
this.Fail("请通过登录页面登录")
|
||||
}
|
||||
var timestampString = params.Token[32:]
|
||||
if stringutil.Md5(TokenSalt+timestampString) != params.Token[:32] {
|
||||
if stringutil.Md5(TokenKey+timestampString) != params.Token[:32] {
|
||||
this.FailField("refresh", "登录页面已过期,请刷新后重试")
|
||||
}
|
||||
var timestamp = types.Int64(timestampString)
|
||||
@@ -123,6 +122,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
this.Fail("服务器出了点小问题:" + err.Error())
|
||||
return
|
||||
}
|
||||
resp, err := rpcClient.AdminRPC().LoginAdmin(rpcClient.Context(0), &pb.LoginAdminRequest{
|
||||
Username: params.Username,
|
||||
@@ -136,6 +136,7 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
actionutils.Fail(this, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.IsOk {
|
||||
@@ -145,31 +146,37 @@ func (this *IndexAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
this.Fail("请输入正确的用户名密码")
|
||||
return
|
||||
}
|
||||
var adminId = resp.AdminId
|
||||
|
||||
// 检查OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: resp.AdminId,
|
||||
Type: "otp",
|
||||
})
|
||||
// 检查是否支持OTP
|
||||
checkOTPResp, err := this.RPC().AdminRPC().CheckAdminOTPWithUsername(this.AdminContext(), &pb.CheckAdminOTPWithUsernameRequest{Username: params.Username})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
|
||||
var loginParams = maps.Map{}
|
||||
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
|
||||
var requireOTP = checkOTPResp.RequireOTP
|
||||
this.Data["requireOTP"] = requireOTP
|
||||
if requireOTP {
|
||||
this.Data["remember"] = params.Remember
|
||||
|
||||
var sid = this.Session().Sid
|
||||
this.Data["sid"] = sid
|
||||
_, err = this.RPC().LoginSessionRPC().WriteLoginSessionValue(this.AdminContext(), &pb.WriteLoginSessionValueRequest{
|
||||
Sid: sid + "_otp",
|
||||
Key: "adminId",
|
||||
Value: types.String(adminId),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
secret := loginParams.GetString("secret")
|
||||
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
|
||||
this.Fail("请输入正确的OTP动态密码")
|
||||
}
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
var adminId = resp.AdminId
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
|
||||
// 记录日志
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Post("/checkOTP", new(CheckOTPAction)).
|
||||
Prefix("/").
|
||||
GetPost("", new(IndexAction)).
|
||||
Prefix("").
|
||||
GetPost("/", new(IndexAction)).
|
||||
GetPost("/index/otp", new(OtpAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
|
||||
154
internal/web/actions/default/index/otp.go
Normal file
154
internal/web/actions/default/index/otp.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/oplogs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/setup"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"github.com/xlzd/gotp"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OtpAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *OtpAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *OtpAction) RunGet(params struct {
|
||||
From string
|
||||
Sid string
|
||||
Remember bool
|
||||
}) {
|
||||
// 检查系统是否已经配置过
|
||||
if !setup.IsConfigured() {
|
||||
this.RedirectURL("/setup")
|
||||
return
|
||||
}
|
||||
|
||||
//// 是否新安装
|
||||
if setup.IsNewInstalled() {
|
||||
this.RedirectURL("/setup/confirm")
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["isUser"] = false
|
||||
this.Data["menu"] = "signIn"
|
||||
|
||||
var timestamp = fmt.Sprintf("%d", time.Now().Unix())
|
||||
this.Data["token"] = stringutil.Md5(TokenKey+timestamp) + timestamp
|
||||
this.Data["from"] = params.From
|
||||
this.Data["sid"] = params.Sid
|
||||
|
||||
uiConfig, err := configloaders.LoadAdminUIConfig()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["systemName"] = uiConfig.AdminSystemName
|
||||
this.Data["showVersion"] = uiConfig.ShowVersion
|
||||
if len(uiConfig.Version) > 0 {
|
||||
this.Data["version"] = uiConfig.Version
|
||||
} else {
|
||||
this.Data["version"] = teaconst.Version
|
||||
}
|
||||
this.Data["faviconFileId"] = uiConfig.FaviconFileId
|
||||
this.Data["remember"] = params.Remember
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *OtpAction) RunPost(params struct {
|
||||
Sid string
|
||||
OtpCode string
|
||||
Remember bool
|
||||
|
||||
Must *actions.Must
|
||||
Auth *helpers.UserShouldAuth
|
||||
}) {
|
||||
if len(params.OtpCode) == 0 {
|
||||
this.FailField("otpCode", "请输入正确的OTP动态密码")
|
||||
return
|
||||
}
|
||||
|
||||
var sid = params.Sid
|
||||
if len(sid) == 0 || len(sid) > 64 {
|
||||
this.Fail("参数错误,请重新登录(001)")
|
||||
return
|
||||
}
|
||||
sid += "_otp"
|
||||
|
||||
// 获取SESSION
|
||||
sessionResp, err := this.RPC().LoginSessionRPC().FindLoginSession(this.AdminContext(), &pb.FindLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var session = sessionResp.LoginSession
|
||||
if session == nil || session.AdminId <= 0 {
|
||||
this.Fail("参数错误,请重新登录(002)")
|
||||
return
|
||||
}
|
||||
var adminId = session.AdminId
|
||||
|
||||
// 检查OTP
|
||||
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
|
||||
AdminId: adminId,
|
||||
Type: "otp",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if otpLoginResp.Login != nil && otpLoginResp.Login.IsOn {
|
||||
var loginParams = maps.Map{}
|
||||
err = json.Unmarshal(otpLoginResp.Login.ParamsJSON, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var secret = loginParams.GetString("secret")
|
||||
if gotp.NewDefaultTOTP(secret).Now() != params.OtpCode {
|
||||
this.FailField("otpCode", "请输入正确的OTP动态密码")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 写入SESSION
|
||||
params.Auth.StoreAdmin(adminId, params.Remember)
|
||||
|
||||
// 删除OTP SESSION
|
||||
_, err = this.RPC().LoginSessionRPC().DeleteLoginSession(this.AdminContext(), &pb.DeleteLoginSessionRequest{Sid: sid})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
err = dao.SharedLogDAO.CreateAdminLog(rpcClient.Context(adminId), oplogs.LevelInfo, this.Request.URL.Path, "成功通过OTP验证登录系统", this.RequestRemoteIP())
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
Reference in New Issue
Block a user