mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-10 08:32:26 +08:00
feat: twofactor bind
This commit is contained in:
@@ -9,11 +9,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal"
|
||||
"github.com/eiblog/eiblog/pkg/middleware"
|
||||
"github.com/eiblog/eiblog/pkg/model"
|
||||
"github.com/eiblog/eiblog/pkg/third/qiniu"
|
||||
"github.com/eiblog/eiblog/tools"
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -46,13 +48,14 @@ func RegisterRoutesAuthz(group gin.IRoutes) {
|
||||
group.POST("/api/trash-recover", handleAPITrashRecover)
|
||||
group.POST("/api/file-upload", handleAPIQiniuUpload)
|
||||
group.POST("/api/file-delete", handleAPIQiniuDelete)
|
||||
group.POST("/api/twofactor", handleAPITwoFactor)
|
||||
}
|
||||
|
||||
// handleAcctLogin 登录接口
|
||||
func handleAcctLogin(c *gin.Context) {
|
||||
user := c.PostForm("user")
|
||||
pwd := c.PostForm("password")
|
||||
// code := c.PostForm("code") // 二次验证
|
||||
code := c.PostForm("code")
|
||||
if user == "" || pwd == "" {
|
||||
logrus.Warnf("参数错误: %s %s", user, pwd)
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
@@ -64,6 +67,16 @@ func handleAcctLogin(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
return
|
||||
}
|
||||
// 两步验证
|
||||
if config.Conf.General.TwoFactor &&
|
||||
internal.Ei.Account.TwoFactorSecret != "" {
|
||||
valid := totp.Validate(code, internal.Ei.Account.TwoFactorSecret)
|
||||
if !valid {
|
||||
logrus.Warnf("两步验证: %s", code)
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
return
|
||||
}
|
||||
}
|
||||
// 登录成功
|
||||
middleware.SetLogin(c, user)
|
||||
|
||||
@@ -76,6 +89,33 @@ func handleAcctLogin(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, "/admin/profile")
|
||||
}
|
||||
|
||||
// handleAPITwoFactor 两步验证
|
||||
func handleAPITwoFactor(c *gin.Context) {
|
||||
code := c.PostForm("code")
|
||||
if code == "" {
|
||||
responseNotice(c, NoticeNotice, "验证码不能为空", "")
|
||||
return
|
||||
}
|
||||
valid := totp.Validate(code, internal.TwoFactorSecret)
|
||||
if !valid {
|
||||
responseNotice(c, NoticeNotice, "验证码错误", "")
|
||||
return
|
||||
}
|
||||
err := internal.Store.UpdateAccount(context.Background(), internal.Ei.Account.Username,
|
||||
map[string]interface{}{
|
||||
"two_factor_secret": internal.TwoFactorSecret,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Error("handleAPITwoFactor.UpdateAccount: ", err)
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
internal.Ei.Account.TwoFactorSecret = internal.TwoFactorSecret
|
||||
internal.TwoFactorSecret = ""
|
||||
c.Request.Header.Set("Referer", "/admin/profile")
|
||||
responseNotice(c, NoticeSuccess, "绑定成功", "")
|
||||
}
|
||||
|
||||
// handleAPIBlogger 更新博客信息
|
||||
func handleAPIBlogger(c *gin.Context) {
|
||||
bn := c.PostForm("blogName")
|
||||
|
||||
@@ -22,8 +22,9 @@ var (
|
||||
XMLTemplate *template.Template // template/xml模板
|
||||
HTMLTemplate *template.Template // website/html模板
|
||||
|
||||
Store store.Store // 数据库存储
|
||||
Ei *Cache // 博客数据缓存
|
||||
Store store.Store // 数据库存储
|
||||
Ei *Cache // 博客数据缓存
|
||||
TwoFactorSecret string // 缓存两步验证密钥
|
||||
|
||||
ESClient *es.ESClient // es 客户端
|
||||
DisqusClient *disqus.DisqusClient // disqus 客户端
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/model"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/clickhouse"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
@@ -63,12 +64,15 @@ func (db *rdbms) Init(name, source string) (Store, error) {
|
||||
return nil, err
|
||||
}
|
||||
// auto migrate
|
||||
gormDB.AutoMigrate(
|
||||
err = gormDB.AutoMigrate(
|
||||
&model.Account{},
|
||||
&model.Blogger{},
|
||||
&model.Article{},
|
||||
&model.Serie{},
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Error("rdbms.AutoMigrate: ", err)
|
||||
}
|
||||
db.DB = gormDB
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package page
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"image/png"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal"
|
||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal/store"
|
||||
"github.com/eiblog/eiblog/pkg/middleware"
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -35,7 +38,11 @@ func handleLoginPage(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, "/admin/profile")
|
||||
return
|
||||
}
|
||||
params := gin.H{"BTitle": internal.Ei.Blogger.BTitle}
|
||||
params := gin.H{
|
||||
"BTitle": internal.Ei.Blogger.BTitle,
|
||||
"TwoFactor": config.Conf.General.TwoFactor &&
|
||||
internal.Ei.Account.TwoFactorSecret != "",
|
||||
}
|
||||
renderHTMLAdminLayout(c, "login.html", params)
|
||||
}
|
||||
|
||||
@@ -46,6 +53,31 @@ func handleAdminProfile(c *gin.Context) {
|
||||
params["Path"] = c.Request.URL.Path
|
||||
params["Console"] = true
|
||||
params["Ei"] = internal.Ei
|
||||
if c.Query("unbind") == "true" {
|
||||
internal.Ei.Account.TwoFactorSecret = ""
|
||||
_ = internal.Store.UpdateAccount(context.Background(), internal.Ei.Account.Username,
|
||||
map[string]interface{}{
|
||||
"two_factor_secret": "",
|
||||
})
|
||||
}
|
||||
if config.Conf.General.TwoFactor &&
|
||||
internal.Ei.Account.TwoFactorSecret == "" {
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: config.Conf.Host,
|
||||
AccountName: internal.Ei.Account.Username,
|
||||
})
|
||||
internal.TwoFactorSecret = key.Secret()
|
||||
if err == nil {
|
||||
var buf bytes.Buffer
|
||||
img, err := key.Image(200, 200)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
png.Encode(&buf, img)
|
||||
b64 := base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||
params["TwoFactorSecret"] = "data:image/png;base64," + b64
|
||||
}
|
||||
}
|
||||
renderHTMLAdminLayout(c, "admin-profile", params)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user