mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 01:57:19 +08:00
feat: 实现安全登录
This commit is contained in:
1
go.mod
1
go.mod
@@ -21,7 +21,6 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a
|
||||
github.com/gomodule/redigo v1.9.2
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0
|
||||
|
||||
54
go.sum
54
go.sum
@@ -62,18 +62,14 @@ github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a h1:BAyyIK6rc6Tq9
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc=
|
||||
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
|
||||
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
@@ -155,7 +151,6 @@ github.com/tufanbarisyildirim/gonginx v0.0.0-20241115180907-128af6df1765 h1:nnw6
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241115180907-128af6df1765/go.mod h1:itu4KWRgrfEwGcfNka+rV4houuirUau53i0diN4lG5g=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -166,77 +161,28 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
|
||||
// 自动注入
|
||||
var (
|
||||
Version string
|
||||
Version = "0.0.0"
|
||||
BuildTime string
|
||||
CommitHash string
|
||||
GoVersion string
|
||||
|
||||
@@ -2,12 +2,15 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
)
|
||||
@@ -16,6 +19,7 @@ import (
|
||||
func MustLogin(next http.Handler) http.Handler {
|
||||
// 白名单
|
||||
whiteList := []string{
|
||||
"/api/user/key",
|
||||
"/api/user/login",
|
||||
"/api/user/logout",
|
||||
"/api/user/isLogin",
|
||||
@@ -57,6 +61,22 @@ func MustLogin(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
safeLogin := cast.ToBool(sess.Get("safe_login"))
|
||||
if safeLogin {
|
||||
safeClientHash := cast.ToString(sess.Get("safe_client"))
|
||||
ip, _, _ := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
|
||||
ua := r.Header.Get("User-Agent")
|
||||
clientHash := fmt.Sprintf("%x", sha3.Sum256([]byte(ip+"|"+ua)))
|
||||
if safeClientHash != clientHash || safeClientHash == "" {
|
||||
render := chix.NewRender(w)
|
||||
render.Status(http.StatusUnauthorized)
|
||||
render.JSON(chix.M{
|
||||
"message": "客户端IP/UA变化,请重新登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
r = r.WithContext(context.WithValue(r.Context(), "user_id", userID)) // nolint:staticcheck
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package request
|
||||
|
||||
type UserLogin struct {
|
||||
Username string `json:"username" form:"username" validate:"required,min=3,max=255"`
|
||||
Password string `json:"password" form:"password" validate:"required,min=6,max=255"`
|
||||
Username string `json:"username" form:"username" validate:"required"`
|
||||
Password string `json:"password" form:"password" validate:"required"`
|
||||
SafeLogin bool `json:"safe_login" form:"safe_login"`
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func Http(r chi.Router) {
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
r.Route("/user", func(r chi.Router) {
|
||||
user := service.NewUserService()
|
||||
r.Get("/key", user.GetKey)
|
||||
r.With(middleware.Throttle(5, time.Minute)).Post("/login", user.Login)
|
||||
r.Post("/logout", user.Logout)
|
||||
r.Get("/isLogin", user.IsLogin)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/collect"
|
||||
@@ -104,8 +103,6 @@ func (s *DashboardService) SystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
time.Now().UTC()
|
||||
|
||||
Success(w, chix.M{
|
||||
"procs": hostInfo.Procs,
|
||||
"hostname": hostInfo.Hostname,
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/rsacrypto"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
@@ -22,6 +29,35 @@ func NewUserService() *UserService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserService) GetKey(w http.ResponseWriter, r *http.Request) {
|
||||
key, err := rsacrypto.GenerateKey()
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
sess, err := app.Session.GetSession(r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
encoded, err := json.Marshal(key)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
sess.Put("key", encoded)
|
||||
|
||||
pk, err := rsacrypto.PublicKeyToString(&key.PublicKey)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, pk)
|
||||
}
|
||||
|
||||
func (s *UserService) Login(w http.ResponseWriter, r *http.Request) {
|
||||
sess, err := app.Session.GetSession(r)
|
||||
if err != nil {
|
||||
@@ -35,21 +71,47 @@ func (s *UserService) Login(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := s.repo.CheckPassword(req.Username, req.Password)
|
||||
key := new(rsa.PrivateKey)
|
||||
if err = json.Unmarshal(sess.Get("key").([]byte), key); err != nil {
|
||||
Error(w, http.StatusForbidden, "invalid key, please refresh the page")
|
||||
return
|
||||
}
|
||||
|
||||
decryptedUsername, _ := rsacrypto.DecryptData(key, req.Username)
|
||||
decryptedPassword, _ := rsacrypto.DecryptData(key, req.Password)
|
||||
user, err := s.repo.CheckPassword(string(decryptedUsername), string(decryptedPassword))
|
||||
if err != nil {
|
||||
Error(w, http.StatusForbidden, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 安全登录模式下,将当前客户端与会话绑定
|
||||
// 安全登录模式只在未启用TLS时生效,因为TLS本身就是安全的
|
||||
ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if req.SafeLogin && !app.Conf.Bool("http.tls") {
|
||||
ua := r.Header.Get("User-Agent")
|
||||
sess.Put("safe_login", true)
|
||||
sess.Put("safe_client", fmt.Sprintf("%x", sha3.Sum256([]byte(ip+"|"+ua))))
|
||||
}
|
||||
|
||||
sess.Put("user_id", user.ID)
|
||||
sess.Forget("key")
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *UserService) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
sess, err := app.Session.GetSession(r)
|
||||
if err == nil {
|
||||
sess.Forget("user_id")
|
||||
if err = sess.Invalidate(); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Success(w, nil)
|
||||
}
|
||||
|
||||
|
||||
85
pkg/rsacrypto/rsacrypto.go
Normal file
85
pkg/rsacrypto/rsacrypto.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package rsacrypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
keySize = 2048 // RSA key size in bits
|
||||
)
|
||||
|
||||
// GenerateKey 生成RSA密钥对
|
||||
func GenerateKey() (*rsa.PrivateKey, error) {
|
||||
return rsa.GenerateKey(rand.Reader, keySize)
|
||||
}
|
||||
|
||||
// EncryptData 加密数据
|
||||
func EncryptData(publicKey *rsa.PublicKey, data []byte) (string, error) {
|
||||
ciphertext, err := rsa.EncryptOAEP(
|
||||
sha512.New(),
|
||||
rand.Reader,
|
||||
publicKey,
|
||||
data,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("encryption failed: %v", err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// DecryptData 解密数据
|
||||
func DecryptData(privateKey *rsa.PrivateKey, ciphertext string) ([]byte, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base64: %v", err)
|
||||
}
|
||||
|
||||
plaintext, err := rsa.DecryptOAEP(
|
||||
sha512.New(),
|
||||
rand.Reader,
|
||||
privateKey,
|
||||
data,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decryption failed: %v", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// PrivateKeyToString 将RSA私钥转换为PEM格式的字符串
|
||||
func PrivateKeyToString(privateKey *rsa.PrivateKey) (string, error) {
|
||||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||
privateKeyPEM := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
},
|
||||
)
|
||||
return string(privateKeyPEM), nil
|
||||
}
|
||||
|
||||
// PublicKeyToString 将RSA公钥转换为PEM格式的字符串
|
||||
func PublicKeyToString(publicKey *rsa.PublicKey) (string, error) {
|
||||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal public key: %v", err)
|
||||
}
|
||||
|
||||
publicKeyPEM := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: publicKeyBytes,
|
||||
},
|
||||
)
|
||||
return string(publicKeyPEM), nil
|
||||
}
|
||||
39
pkg/rsacrypto/rsacrypto_test.go
Normal file
39
pkg/rsacrypto/rsacrypto_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package rsacrypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type RSATestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestRSATestSuite(t *testing.T) {
|
||||
suite.Run(t, &RSATestSuite{})
|
||||
}
|
||||
|
||||
func (suite *RSATestSuite) TestRSA() {
|
||||
// 生成RSA密钥对
|
||||
privateKey, err := GenerateKey()
|
||||
suite.NoError(err)
|
||||
suite.NotEmpty(privateKey)
|
||||
suite.NotEmpty(privateKey.PublicKey)
|
||||
|
||||
// 提取密钥对
|
||||
suite.NotEmpty(PrivateKeyToString(privateKey))
|
||||
suite.NotEmpty(PublicKeyToString(&privateKey.PublicKey))
|
||||
|
||||
message := []byte("Rat Panel")
|
||||
|
||||
// 加密数据
|
||||
ciphertext, err := EncryptData(&privateKey.PublicKey, message)
|
||||
suite.NoError(err)
|
||||
suite.NotEmpty(ciphertext)
|
||||
|
||||
// 解密数据
|
||||
decrypted, err := DecryptData(privateKey, ciphertext)
|
||||
suite.NoError(err)
|
||||
suite.NotEmpty(decrypted)
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
"luxon": "^3.5.0",
|
||||
"marked": "^15.0.2",
|
||||
"mitt": "^3.0.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"pinia": "^2.2.6",
|
||||
"pinia-plugin-persistedstate": "^4.1.3",
|
||||
"remove": "^0.1.5",
|
||||
@@ -57,6 +58,7 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^22.9.1",
|
||||
"@types/node-forge": "^1.3.11",
|
||||
"@unocss/eslint-config": "^0.65.0",
|
||||
"@vitejs/plugin-vue": "^5.2.0",
|
||||
"@vue/eslint-config-prettier": "^10.1.0",
|
||||
|
||||
34
web/pnpm-lock.yaml
generated
34
web/pnpm-lock.yaml
generated
@@ -71,6 +71,9 @@ importers:
|
||||
mitt:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
node-forge:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
pinia:
|
||||
specifier: ^2.2.6
|
||||
version: 2.2.8(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))
|
||||
@@ -114,6 +117,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^22.9.1
|
||||
version: 22.10.1
|
||||
'@types/node-forge':
|
||||
specifier: ^1.3.11
|
||||
version: 1.3.11
|
||||
'@unocss/eslint-config':
|
||||
specifier: ^0.65.0
|
||||
version: 0.65.0(eslint@9.16.0(jiti@2.4.1))(typescript@5.6.3)
|
||||
@@ -966,36 +972,42 @@ packages:
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.0':
|
||||
resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.0':
|
||||
resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.0':
|
||||
resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.0':
|
||||
resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.0':
|
||||
resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.0':
|
||||
resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==}
|
||||
@@ -1073,46 +1085,55 @@ packages:
|
||||
resolution: {integrity: sha512-WXveUPKtfqtaNvpf0iOb0M6xC64GzUX/OowbqfiCSXTdi/jLlOmH0Ba94/OkiY2yTGTwteo4/dsHRfh5bDCZ+w==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.28.0':
|
||||
resolution: {integrity: sha512-yLc3O2NtOQR67lI79zsSc7lk31xjwcaocvdD1twL64PK1yNaIqCeWI9L5B4MFPAVGEVjH5k1oWSGuYX1Wutxpg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.28.0':
|
||||
resolution: {integrity: sha512-+P9G9hjEpHucHRXqesY+3X9hD2wh0iNnJXX/QhS/J5vTdG6VhNYMxJ2rJkQOxRUd17u5mbMLHM7yWGZdAASfcg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.28.0':
|
||||
resolution: {integrity: sha512-1xsm2rCKSTpKzi5/ypT5wfc+4bOGa/9yI/eaOLW0oMs7qpC542APWhl4A37AENGZ6St6GBMWhCCMM6tXgTIplw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.28.0':
|
||||
resolution: {integrity: sha512-zgWxMq8neVQeXL+ouSf6S7DoNeo6EPgi1eeqHXVKQxqPy1B2NvTbaOUWPn/7CfMKL7xvhV0/+fq/Z/J69g1WAQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.28.0':
|
||||
resolution: {integrity: sha512-VEdVYacLniRxbRJLNtzwGt5vwS0ycYshofI7cWAfj7Vg5asqj+pt+Q6x4n+AONSZW/kVm+5nklde0qs2EUwU2g==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.28.0':
|
||||
resolution: {integrity: sha512-LQlP5t2hcDJh8HV8RELD9/xlYtEzJkm/aWGsauvdO2ulfl3QYRjqrKW+mGAIWP5kdNCBheqqqYIGElSRCaXfpw==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.28.0':
|
||||
resolution: {integrity: sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.28.0':
|
||||
resolution: {integrity: sha512-eKpJr4vBDOi4goT75MvW+0dXcNUqisK4jvibY9vDdlgLx+yekxSm55StsHbxUsRxSTt3JEQvlr3cGDkzcSP8bw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.28.0':
|
||||
resolution: {integrity: sha512-Vi+WR62xWGsE/Oj+mD0FNAPY2MEox3cfyG0zLpotZdehPFXwz6lypkGs5y38Jd/NVSbOD02aVad6q6QYF7i8Bg==}
|
||||
@@ -1173,6 +1194,9 @@ packages:
|
||||
'@types/mdurl@2.0.0':
|
||||
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||
|
||||
'@types/node@22.10.1':
|
||||
resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==}
|
||||
|
||||
@@ -2705,6 +2729,10 @@ packages:
|
||||
node-fetch-native@1.6.4:
|
||||
resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==}
|
||||
|
||||
node-forge@1.3.1:
|
||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
|
||||
node-html-parser@5.4.2:
|
||||
resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==}
|
||||
|
||||
@@ -4641,6 +4669,10 @@ snapshots:
|
||||
|
||||
'@types/mdurl@2.0.0': {}
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
dependencies:
|
||||
'@types/node': 22.10.1
|
||||
|
||||
'@types/node@22.10.1':
|
||||
dependencies:
|
||||
undici-types: 6.20.0
|
||||
@@ -6442,6 +6474,8 @@ snapshots:
|
||||
|
||||
node-fetch-native@1.6.4: {}
|
||||
|
||||
node-forge@1.3.1: {}
|
||||
|
||||
node-html-parser@5.4.2:
|
||||
dependencies:
|
||||
css-select: 4.3.0
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
import { request } from '@/utils'
|
||||
import { http } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 公钥
|
||||
key: () => http.Get('/user/key'),
|
||||
// 登录
|
||||
login: (username: string, password: string): Promise<AxiosResponse<any>> =>
|
||||
request.post('/user/login', {
|
||||
login: (username: string, password: string) =>
|
||||
http.Post('/user/login', {
|
||||
username,
|
||||
password
|
||||
}),
|
||||
// 登出
|
||||
logout: (): Promise<AxiosResponse<any>> => request.post('/user/logout'),
|
||||
logout: () => http.Post('/user/logout'),
|
||||
// 是否登录
|
||||
isLogin: (): Promise<AxiosResponse<any>> => request.get('/user/isLogin'),
|
||||
isLogin: () => http.Get('/user/isLogin'),
|
||||
// 获取用户信息
|
||||
info: (): Promise<AxiosResponse<any>> => request.get('/user/info')
|
||||
info: () => http.Get('/user/info')
|
||||
}
|
||||
|
||||
1
web/src/utils/encrypt/index.ts
Normal file
1
web/src/utils/encrypt/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './rsa'
|
||||
16
web/src/utils/encrypt/rsa.ts
Normal file
16
web/src/utils/encrypt/rsa.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import * as forge from 'node-forge'
|
||||
|
||||
export function rsaEncrypt(data: string, publicKey: string) {
|
||||
const pk = forge.pki.publicKeyFromPem(publicKey)
|
||||
const encryptedBytes = pk.encrypt(data, 'RSA-OAEP', {
|
||||
md: forge.md.sha512.create()
|
||||
})
|
||||
return forge.util.encode64(encryptedBytes)
|
||||
}
|
||||
|
||||
export function rsaDecrypt(data: string, privateKey: string) {
|
||||
const pk = forge.pki.privateKeyFromPem(privateKey)
|
||||
return pk.decrypt(forge.util.decode64(data), 'RSA-OAEP', {
|
||||
md: forge.md.sha512.create()
|
||||
})
|
||||
}
|
||||
@@ -4,19 +4,24 @@ import bgImg from '@/assets/images/login_bg.webp'
|
||||
import { addDynamicRoutes } from '@/router'
|
||||
import { useThemeStore, useUserStore } from '@/store'
|
||||
import { getLocal, removeLocal, setLocal } from '@/utils'
|
||||
import { rsaEncrypt } from '@/utils/encrypt'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const query = route.query
|
||||
const { data: key, loading: isLoading } = useRequest(user.key, { initialData: '' })
|
||||
const { data: isLogin } = useRequest(user.isLogin, { initialData: false })
|
||||
|
||||
interface LoginInfo {
|
||||
username: string
|
||||
password: string
|
||||
safe_login: boolean
|
||||
}
|
||||
|
||||
const loginInfo = ref<LoginInfo>({
|
||||
username: '',
|
||||
password: ''
|
||||
password: '',
|
||||
safe_login: true
|
||||
})
|
||||
|
||||
const localLoginInfo = getLocal('loginInfo') as LoginInfo
|
||||
@@ -36,40 +41,49 @@ async function handleLogin() {
|
||||
window.$message.warning('请输入用户名和密码')
|
||||
return
|
||||
}
|
||||
if (!key) {
|
||||
window.$message.warning('获取加密公钥失败,请刷新页面重试')
|
||||
return
|
||||
}
|
||||
try {
|
||||
user.login(username, password).then(async () => {
|
||||
loging.value = true
|
||||
window.$notification?.success({ title: '登录成功!', duration: 2500 })
|
||||
if (isRemember.value) {
|
||||
setLocal('loginInfo', { username, password })
|
||||
} else {
|
||||
removeLocal('loginInfo')
|
||||
}
|
||||
user
|
||||
.login(rsaEncrypt(username, String(unref(key))), rsaEncrypt(password, String(unref(key))))
|
||||
.then(async () => {
|
||||
loging.value = true
|
||||
window.$notification?.success({ title: '登录成功!', duration: 2500 })
|
||||
if (isRemember.value) {
|
||||
setLocal('loginInfo', { username, password })
|
||||
} else {
|
||||
removeLocal('loginInfo')
|
||||
}
|
||||
|
||||
await addDynamicRoutes()
|
||||
const { data } = await user.info()
|
||||
userStore.set(data)
|
||||
if (query.redirect) {
|
||||
const path = query.redirect as string
|
||||
Reflect.deleteProperty(query, 'redirect')
|
||||
await router.push({ path, query })
|
||||
} else {
|
||||
await router.push('/')
|
||||
}
|
||||
})
|
||||
await addDynamicRoutes()
|
||||
await user.info().then((data: any) => {
|
||||
userStore.set(data)
|
||||
})
|
||||
if (query.redirect) {
|
||||
const path = query.redirect as string
|
||||
Reflect.deleteProperty(query, 'redirect')
|
||||
await router.push({ path, query })
|
||||
} else {
|
||||
await router.push('/')
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
loging.value = false
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 已登录自动跳转
|
||||
await user.isLogin().then(async (res) => {
|
||||
if (res.data) {
|
||||
watch(
|
||||
() => isLogin,
|
||||
async () => {
|
||||
if (isLogin) {
|
||||
console.log(isLogin)
|
||||
await addDynamicRoutes()
|
||||
const { data } = await user.info()
|
||||
userStore.set(data)
|
||||
await user.info().then((data: any) => {
|
||||
userStore.set(data)
|
||||
})
|
||||
if (query.redirect) {
|
||||
const path = query.redirect as string
|
||||
Reflect.deleteProperty(query, 'redirect')
|
||||
@@ -78,8 +92,8 @@ onMounted(async () => {
|
||||
await router.push('/')
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -111,16 +125,16 @@ onMounted(async () => {
|
||||
</div>
|
||||
|
||||
<div mt-20>
|
||||
<n-checkbox
|
||||
:checked="isRemember"
|
||||
:on-update:checked="(val: boolean) => (isRemember = val)"
|
||||
label="记住我"
|
||||
/>
|
||||
<n-flex>
|
||||
<n-checkbox v-model:checked="loginInfo.safe_login" label="安全登录" />
|
||||
<n-checkbox v-model:checked="isRemember" label="记住我" />
|
||||
</n-flex>
|
||||
</div>
|
||||
|
||||
<div mt-20>
|
||||
<n-button
|
||||
:loading="loging"
|
||||
:loading="isLoading || loging"
|
||||
:disabled="isLoading || loging"
|
||||
type="primary"
|
||||
h-50
|
||||
w-full
|
||||
|
||||
Reference in New Issue
Block a user