:building:修改二次验证方式
This commit is contained in:
@@ -20,7 +20,10 @@ func (c *TotpQuery) GenTotp() {
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
|
||||
}
|
||||
newTotp, err := c.GetInt("newTotp")
|
||||
if err != nil {
|
||||
newTotp = 0
|
||||
}
|
||||
|
||||
userInfo := user.GetUserInfoByUserID(userID)
|
||||
@@ -33,7 +36,11 @@ func (c *TotpQuery) GenTotp() {
|
||||
return
|
||||
}
|
||||
|
||||
otp, err := mfa.GetOtp(userInfo.UserId, userInfo.Nick)
|
||||
otpSecret := ""
|
||||
if userInfo.OtpSecret != "" && newTotp == 0 {
|
||||
otpSecret = userInfo.OtpSecret
|
||||
}
|
||||
otp, err := mfa.GetOtp(userInfo.UserId, userInfo.Nick, otpSecret, userInfo.OtpKey)
|
||||
if err != nil {
|
||||
c.Data["json"] = datas.BaseDataJSON{
|
||||
Code: -1,
|
||||
@@ -41,7 +48,6 @@ func (c *TotpQuery) GenTotp() {
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
c.Data["json"] = datas.KeyDataJSON2{
|
||||
@@ -58,8 +64,9 @@ func (c *TotpQuery) GenTotp() {
|
||||
func (c *TotpQuery) SaveTotp() {
|
||||
code := c.GetString("code")
|
||||
secret := c.GetString("secret")
|
||||
key := c.GetString("key")
|
||||
|
||||
if code == "" || secret == "" {
|
||||
if code == "" || secret == "" || key == "" {
|
||||
c.Data["json"] = datas.BaseDataJSON{
|
||||
Code: -1,
|
||||
Msg: "提交消息不准确",
|
||||
@@ -67,7 +74,6 @@ func (c *TotpQuery) SaveTotp() {
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := c.GetSession("userID").(string)
|
||||
if !ok {
|
||||
c.Data["json"] = datas.BaseDataJSON{
|
||||
@@ -76,7 +82,6 @@ func (c *TotpQuery) SaveTotp() {
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
userInfo := user.GetUserInfoByUserID(userID)
|
||||
@@ -102,7 +107,7 @@ func (c *TotpQuery) SaveTotp() {
|
||||
|
||||
}
|
||||
|
||||
err2 := user.UpdateOtpByUserID(userInfo.UserId, secret)
|
||||
err2 := user.UpdateOtpByUserID(userInfo.UserId, secret, key)
|
||||
if err2 != nil {
|
||||
c.Data["json"] = datas.BaseDataJSON{
|
||||
Code: -1,
|
||||
|
||||
2
go.mod
2
go.mod
@@ -6,10 +6,12 @@ require (
|
||||
github.com/beego/beego/v2 v2.0.2
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/gogf/gf v1.16.9
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/xlzd/gotp v0.1.0
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -109,6 +109,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -126,7 +127,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
@@ -405,6 +405,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -554,7 +555,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
|
||||
@@ -32,6 +32,7 @@ type UserInfo struct {
|
||||
CreateTime string
|
||||
UpdateTime string
|
||||
OtpSecret string
|
||||
OtpKey string
|
||||
}
|
||||
|
||||
func GetUserInfoByUserID(userID string) UserInfo {
|
||||
@@ -44,11 +45,11 @@ func GetUserInfoByUserID(userID string) UserInfo {
|
||||
return userInfo
|
||||
}
|
||||
|
||||
func UpdateOtpByUserID(userID, totpSecret string) error {
|
||||
func UpdateOtpByUserID(userID, totpSecret, key string) error {
|
||||
o := orm.NewOrm()
|
||||
_, err := o.QueryTable(USERINFO).Exclude("status", "delete").
|
||||
Filter("user_id", userID).
|
||||
Update(orm.Params{"otp_secret": totpSecret})
|
||||
Update(orm.Params{"otp_secret": totpSecret, "otp_key": key})
|
||||
|
||||
if err != nil {
|
||||
logs.Error("更新totp失败", err)
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
/***************************************************
|
||||
** @Desc : generate login verify code image
|
||||
** @Time : 2019/8/7 17:14
|
||||
** @Author : yuebin
|
||||
** @File : login_verify_code
|
||||
** @Last Modified by : yuebin
|
||||
** @Last Modified time: 2019/8/7 17:14
|
||||
** @Software: GoLand
|
||||
****************************************************/
|
||||
package utils
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
/***************************************************
|
||||
** @Desc : 获取一个md5的字符串
|
||||
** @Time : 2019/8/9 16:06
|
||||
** @Author : yuebin
|
||||
** @File : md5
|
||||
** @Last Modified by : yuebin
|
||||
** @Last Modified time: 2019/8/9 16:06
|
||||
** @Software: GoLand
|
||||
****************************************************/
|
||||
package utils
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mfa
|
||||
|
||||
import (
|
||||
"boss/utils"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
@@ -17,13 +18,17 @@ const secretLength = 16
|
||||
type Otp struct {
|
||||
Secret string `json:"secret"`
|
||||
QrImage string `json:"qrImage"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
func GetOtp(userId, username string) (otp Otp, err error) {
|
||||
secret := gotp.RandomSecret(secretLength)
|
||||
func GetOtp(userId, username, secret, key string) (otp Otp, err error) {
|
||||
if secret == "" {
|
||||
secret = gotp.RandomSecret(secretLength)
|
||||
key = utils.RandomString(6)
|
||||
}
|
||||
otp.Secret = secret
|
||||
totp := gotp.NewDefaultTOTP(secret)
|
||||
uri := totp.ProvisioningUri(userId, fmt.Sprintf("卡销平台 管理员 %s", username))
|
||||
uri := totp.ProvisioningUri(userId, fmt.Sprintf("卡销平台 管理员 %s %s", username, key))
|
||||
uri, err = url.PathUnescape(uri)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -34,6 +39,7 @@ func GetOtp(userId, username string) (otp Otp, err error) {
|
||||
index := bytes.IndexByte(dist, 0)
|
||||
baseImage := dist[0:index]
|
||||
otp.QrImage = "data:image/png;base64," + string(baseImage)
|
||||
otp.Key = key
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
11
utils/mfa/mfa_test.go
Normal file
11
utils/mfa/mfa_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package mfa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetOtp(t *testing.T) {
|
||||
otp, _ := GetOtp("10086", "admin", "TT37BPUE3RJOT372X3ZNX5HM7Y")
|
||||
fmt.Println(otp.QrImage)
|
||||
}
|
||||
16
utils/random.go
Normal file
16
utils/random.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RandomString(n int) string {
|
||||
const charset = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
sb := strings.Builder{}
|
||||
sb.Grow(n)
|
||||
for i := 0; i < n; i++ {
|
||||
sb.WriteByte(charset[rand.Intn(len(charset))])
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -1,12 +1,3 @@
|
||||
/***************************************************
|
||||
** @Desc : This file for ...
|
||||
** @Time : 2019/10/26 11:08
|
||||
** @Author : yuebin
|
||||
** @File : sign_verify
|
||||
** @Last Modified by : yuebin
|
||||
** @Last Modified time: 2019/10/26 11:08
|
||||
** @Software: GoLand
|
||||
****************************************************/
|
||||
package utils
|
||||
|
||||
func GetMD5Sign(params map[string]string, keys []string, paySecret string) string {
|
||||
|
||||
@@ -193,18 +193,24 @@
|
||||
<h4 class="modal-title" id="totpLabel">TOTP二次验证</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row margin-top-20">
|
||||
<div class="col-xs-5">
|
||||
<label>请输入二次验证:
|
||||
<input id="totp-value" type="text" name="">
|
||||
</label>
|
||||
<div class="totp-regeneration">
|
||||
<div class="row">
|
||||
当前标识:<span id="totp-key"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button class="btn btn-warning totp-regeneration-btn" data-toggle="tooltip"
|
||||
title="重新生成将导致此前的二次验证不可用,请谨慎生成">重新生成
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row margin-top-20 totp-body">
|
||||
<div id="totp-img">
|
||||
<img src="" alt="" srcset="">
|
||||
<input value="" id="totp-secret" hidden>
|
||||
</div>
|
||||
<label>请输入二次验证:
|
||||
<input id="totp-value" type="text" name="">
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-4 color-red totp-new-error">
|
||||
</div>
|
||||
@@ -506,11 +512,11 @@
|
||||
});
|
||||
});
|
||||
|
||||
$(".logout").click(function () {
|
||||
$(".logout").click(() => {
|
||||
$.ajax({
|
||||
url: "/logout",
|
||||
success: function (res) {
|
||||
if (res.Code == 200) {
|
||||
if (res.Code === 200) {
|
||||
window.parent.location = "/login.html";
|
||||
} else {
|
||||
alert("系统异常,退出失败!");
|
||||
@@ -565,31 +571,51 @@
|
||||
if (res.Code === 0) {
|
||||
$("#totp-img img").attr("src", res.Data.qrImage);
|
||||
$("#totp-secret").attr("value", res.Data.secret);
|
||||
$("#totp-key").text(res.Data.key);
|
||||
} else {
|
||||
alert(res.Msg)
|
||||
}
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
$(".totp-regeneration-btn").click(() => {
|
||||
$.ajax({
|
||||
url: "/user/genTotp",
|
||||
data: {
|
||||
newTotp: 1,
|
||||
},
|
||||
method: "post",
|
||||
success: (res) => {
|
||||
if (res.Code === 0) {
|
||||
$("#totp-img img").attr("src", res.Data.qrImage);
|
||||
$("#totp-secret").attr("value", res.Data.secret);
|
||||
$("#totp-key").text(res.Data.key);
|
||||
} else {
|
||||
alert(res.Msg)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
$(".totp-save").click(function (event) {
|
||||
// 获取secret
|
||||
const secret = $("#totp-secret").val();
|
||||
const code = $("#totp-value").val();
|
||||
if (secret === "" || code === "") {
|
||||
const key = $("#totp-key").text();
|
||||
console.log(secret, code, key)
|
||||
if (secret === "" || code === "" || key === "") {
|
||||
setError(".totp-new-error", "信息填写不完整,需要重新填写");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/user/saveTotp",
|
||||
method: "post",
|
||||
data: {
|
||||
code: code,
|
||||
secret: secret,
|
||||
code,
|
||||
secret,
|
||||
key,
|
||||
},
|
||||
success: (res) => {
|
||||
if (res.Code === 0) {
|
||||
$("#totp-secret").val("");
|
||||
$("#totp-value").val("");
|
||||
$(".cancal-save").trigger('click');
|
||||
alert("保存成功!")
|
||||
@@ -603,5 +629,16 @@
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.totp-regeneration {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.totp-body {
|
||||
margin: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
||||
@@ -1308,7 +1308,7 @@
|
||||
"agentName": agentName
|
||||
},
|
||||
success: function (res) {
|
||||
if (res.Code == 404) {
|
||||
if (res.Code === 404) {
|
||||
window.parent.location = "/login.html";
|
||||
} else if (res.Code == -1) {
|
||||
} else {
|
||||
@@ -1512,7 +1512,6 @@
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
$("#pay-type").val("CARD_DH")
|
||||
|
||||
Reference in New Issue
Block a user