From d2b118c1897afc30522eca903b3b88c0bd189e54 Mon Sep 17 00:00:00 2001 From: HuangZhi Date: Tue, 11 Jul 2023 14:54:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=98=E8=AE=B0=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 5 +- go.sum | 16 ++- src/bean/vo/request/login.go | 15 ++ src/bean/vo/response/system_menu.go | 35 +++-- src/common/conf/options.go | 19 ++- src/controller/login.go | 208 +++++++++++++++++++++++----- src/main.go | 4 + src/router/loginrouter.go | 15 +- src/service/login.go | 197 +++++++++++++++++++++++--- src/util/common.go | 13 ++ 10 files changed, 450 insertions(+), 77 deletions(-) diff --git a/go.mod b/go.mod index e5a5dff..212e98d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/360EntSecGroup-Skylar/excelize v1.4.1 github.com/Luzifer/go-openssl/v4 v4.1.0 + github.com/aliyun/alibaba-cloud-sdk-go v1.62.431 github.com/bytedance/go-tagexpr v2.7.4+incompatible github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.9.0 @@ -46,6 +47,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/henrylee2cn/ameda v1.5.0 // indirect github.com/henrylee2cn/goutil v0.0.0-20220704075712-42f2ec55fe8d // indirect + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.3 // indirect @@ -57,6 +59,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nyaruka/phonenumbers v1.1.7 // indirect + github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sirupsen/logrus v1.9.2 // indirect @@ -64,7 +67,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect golang.org/x/crypto v0.9.0 // indirect diff --git a/go.sum b/go.sum index 2096a57..4719f4b 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v1.62.431 h1:P4qW1rrPjPDIvVc8OHNvhOXawvWD/C6opsvNYTDcXF0= +github.com/aliyun/alibaba-cloud-sdk-go v1.62.431/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs= github.com/andeya/goutil v0.0.0-20220704075712-42f2ec55fe8d h1:qZjX5KRJDCA0DaORmzyXuySdlT+MOhx0OOTbUbdPxp0= github.com/andeya/goutil v0.0.0-20220704075712-42f2ec55fe8d/go.mod h1:jEG5/QnnhG7yGxwFUX6Q+JGMif7sjdHmmNVjn7nhJDo= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= @@ -115,6 +117,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -231,9 +234,11 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -343,6 +348,8 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= @@ -446,6 +453,10 @@ github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xz github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -467,8 +478,9 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -642,6 +654,7 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -656,6 +669,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/src/bean/vo/request/login.go b/src/bean/vo/request/login.go index 695143c..b3bbb05 100644 --- a/src/bean/vo/request/login.go +++ b/src/bean/vo/request/login.go @@ -33,3 +33,18 @@ type UserOrgInfo struct { SystemId string `json:"system_id" xorm:"system_id"` //系统账号id IsAdmin int `json:"is_admin" xorm:"is_admin"` //用户类型 } + +type SendPhoneMsg struct { + Phone string `json:"phone"` + Code string `json:"code"` +} + +type ForgetPwdCheck struct { + Phone string `json:"phone" vd:"len($)>0;msg:'手机号不能为空'"` // 手机号 + Code string `json:"code" vd:"len($)=6;msg:'验证码格式不正确'"` // 验证码 +} + +type AccountForgetPasswordReq struct { + Phone string `json:"phone" vd:"len($)>0;msg:'手机号不能为空'"` // 系统账号 + Password string `json:"password" vd:"len($)>0;msg:'请输入密码'"` // 密码 +} diff --git a/src/bean/vo/response/system_menu.go b/src/bean/vo/response/system_menu.go index ebba72e..f463b2b 100644 --- a/src/bean/vo/response/system_menu.go +++ b/src/bean/vo/response/system_menu.go @@ -23,23 +23,30 @@ type SystemMenuTree struct { PMenuId string `json:"p_menu_id" xorm:"p_menu_id"` //上级菜单id Source string `json:"source" xorm:"source"` //源 SystemType string `json:"system_type"` //系统类型 + NewWindow int `json:"new_window"` //是否开启新窗口(0 不开 1开启) + Remark string `json:"remark"` //备注说明 + BuiltIn int `json:"built_in"` //是否内置 0 否 1内置数据 Child []SystemMenuTree `xorm:"-"` } // 系统菜单表 type SystemMenuTreePer struct { - Id int32 `json:"id" xorm:"pk autoincr" ` //id - Level int `json:"level" xorm:"level"` //菜单等级 - Sort int `json:"sort" xorm:"sort"` //菜单排序 - MenuName string `json:"menuName" xorm:"menu_name"` //菜单名称 - DictGroupId string `json:"dict_group_id" xorm:"dict_group_id"` //字典分类id - MenuType int `json:"menuType" xorm:"menu_type"` //菜单类型(0目录1菜单2子页面) - Path string `json:"path" xorm:"menu_url"` //菜单路径 - ParentPath string `json:"parentPath" xorm:"-"` - Icon string `json:"icon" xorm:"menu_logo"` //菜单图标 - MenuId string `json:"menu_id" xorm:"menu_id"` //菜单id - PMenuId string `json:"p_menu_id" xorm:"p_menu_id"` //上级菜单id - SystemType string `json:"system_type"` //系统类型 - Source string `json:"source" xorm:"source"` //源 - Children []SystemMenuTreePer `json:"children" xorm:"-"` + Id int32 `json:"id" xorm:"pk autoincr" ` //id + Level int `json:"level" xorm:"level"` //菜单等级 + Sort int `json:"sort" xorm:"sort"` //菜单排序 + MenuName string `json:"menuName" xorm:"menu_name"` //菜单名称 + DictGroupId string `json:"dict_group_id" xorm:"dict_group_id"` //字典分类id + MenuType int `json:"menuType" xorm:"menu_type"` //菜单类型(0目录1菜单2子页面) + Path string `json:"path" xorm:"menu_url"` //菜单路径 + ParentPath string `json:"parentPath" xorm:"-"` + Icon string `json:"icon" xorm:"menu_logo"` //菜单图标 + MenuId string `json:"menu_id" xorm:"menu_id"` //菜单id + PMenuId string `json:"p_menu_id" xorm:"p_menu_id"` //上级菜单id + SystemType string `json:"system_type"` //系统类型 + Source string `json:"source" xorm:"source"` //源 + NewWindow int `json:"new_window"` //是否开启新窗口(0 不开 1开启) + Remark string `json:"remark"` //备注说明 + BuiltIn int `json:"built_in"` //是否内置 0 否 1内置数据 + DisplayCondition int `json:"-"` //展示条件(0 登录 1免登录) + Children []SystemMenuTreePer `json:"children" xorm:"-"` } diff --git a/src/common/conf/options.go b/src/common/conf/options.go index 3b8fff9..89228cb 100644 --- a/src/common/conf/options.go +++ b/src/common/conf/options.go @@ -37,14 +37,19 @@ type Config struct { LocationKey string PrivateKeySSH string PublicKeySSH string + SmsAccessKeyId string + SmsAccessSecret string + SmsTemplateLogin string + SmsSignName string } const ( - LockDuration = 1 * time.Hour //锁定时间 默认 1小时 - LockErrorNumber = 3 //错误次数 3次 - WIDTH = 240 - HEIGHT = 60 - CookieName = "bgToken" - CookieNameLastLogin string = "lastLogin" - LocalDateTimeFormat string = "2006-01-02 15:04:05" + LockDuration = 1 * time.Hour //锁定时间 默认 1小时 + LockErrorNumber = 3 //错误次数 3次 + WIDTH = 240 + HEIGHT = 60 + CookieName = "bgToken" + CookieNameLastLogin string = "lastLogin" + LocalDateTimeFormat string = "2006-01-02 15:04:05" + FinalHeartBeatUnixKey = "FinalHeartBeatUnix" ) diff --git a/src/controller/login.go b/src/controller/login.go index 4775b93..d72b114 100644 --- a/src/controller/login.go +++ b/src/controller/login.go @@ -6,7 +6,9 @@ import ( "github.com/gin-gonic/gin" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/constant" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "go.uber.org/zap" @@ -91,44 +93,45 @@ func UserLoginV2(c *gin.Context) { SendJsonResponse(c, err, nil) return } - err = errors.New("密码错误") - SendJsonResponse(c, err, nil) - return - } - loginInf.UserUnLock(userInfo.Id) - - // TODO STEP2 用户和ip没有加入到访问规则登录不上提醒 - if config.AccessRuleState == 1 { - ip := util.RemoteIp(c.Request) - svc := service.AccessRuleSvc{} - if errCode := svc.CheckIp(ip, userInfo.Id); errCode != nil { - SendJsonResponse(c, errCode, nil) - return - } - } - // 密码失效提醒 - subDays := util.SubDays(time.Now(), userInfo.PwdUpdatedTime) - if config.PwdValidity-subDays <= 0 && config.PwdValidity != 0 && userInfo.PwdUpdatedTime.String()[:10] != "0001-01-01" { - SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码已失效,请联系管理员修改密码!")), nil) - return - } - // 密码强弱提醒 - if config.ForceUpdateState == 1 && config.MinPwdLevel > userInfo.PwdLevel { - SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码强度弱,请联系管理员修改密码!")), nil) - return } + err = errors.New("密码错误") + SendJsonResponse(c, err, nil) + return + } + loginInf.UserUnLock(userInfo.Id) - lastLogin, msg, uuidStr, err := loginInf.LoginV2(userInfo) - if err != nil { - SendJsonResponse(c, err, "") + // TODO STEP2 用户和ip没有加入到访问规则登录不上提醒 + if config.AccessRuleState == 1 { + ip := util.RemoteIp(c.Request) + svc := service.AccessRuleSvc{} + if errCode := svc.CheckIp(ip, userInfo.Id); errCode != nil { + SendJsonResponse(c, errCode, nil) return } - c.SetCookie(conf.CookieName, uuidStr, 1*60*60*24, "/", "", false, false) - c.SetCookie(conf.CookieNameLastLogin, lastLogin, 1*60*60*24, "/", "", false, false) + } + // 密码失效提醒 + subDays := util.SubDays(time.Now(), userInfo.PwdUpdatedTime) + if config.PwdValidity-subDays <= 0 && config.PwdValidity != 0 && userInfo.PwdUpdatedTime.String()[:10] != "0001-01-01" { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码已失效,请联系管理员修改密码!")), nil) + return + } + // 密码强弱提醒 + if config.ForceUpdateState == 1 && config.MinPwdLevel > userInfo.PwdLevel { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码强度弱,请联系管理员修改密码!")), nil) + return + } - SendJsonResponse(c, resp.OK, msg) + lastLogin, msg, uuidStr, err := loginInf.LoginV2(userInfo) + if err != nil { + SendJsonResponse(c, err, "") + return } + c.SetCookie(conf.CookieName, uuidStr, 1*60*60*24, "/", "", false, false) + c.SetCookie(conf.CookieNameLastLogin, lastLogin, 1*60*60*24, "/", "", false, false) + + SendJsonResponse(c, resp.OK, msg) } + func GetUserInfo(c *gin.Context) { token, _ := c.Cookie(conf.CookieName) @@ -199,3 +202,146 @@ func UserLogout(c *gin.Context) { } SendJsonResponse(c, resp.OK, msg) } + +// 获取短信验证码 +func GetSmsVerifyCode(c *gin.Context) { + req := request.SendPhoneMsg{} + err := c.BindJSON(&req) + if err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), nil) + return + } + if !util.CheckMobile(req.Phone) { + SendJsonResponse(c, errors.New("手机号不合法!"), nil) + return + } + loginInf := service.UserSvc{} + code, err := loginInf.GetSmsVerifyCode(req.Phone) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonResponse(c, resp.OK, code) +} + +func UserPhoneLogin(c *gin.Context) { + req := request.SendPhoneMsg{} + err := c.BindJSON(&req) + if err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), nil) + return + } + if !util.CheckMobile(req.Phone) { + SendJsonResponse(c, errors.New("手机号不合法!"), nil) + return + } + loginInf := service.UserSvc{} + userInfo, err := loginInf.GetUserInfo(req.Phone) + if req.Phone != userInfo.Phone { + SendJsonResponse(c, resp.DbSelectError.ErrorDetail(errors.New("手机号不正确!")), nil) + return + } + // 获取系统配置信息 + op := service.SystemOptionsSvc{} + config, err := op.GetSystemOptions() + if err != nil { + SendJsonResponse(c, err, nil) + return + } + + // TODO STEP2 用户和ip没有加入到访问规则登录不上提醒 + if config.AccessRuleState == 1 { + ip := util.RemoteIp(c.Request) + svc := service.AccessRuleSvc{} + if errCode := svc.CheckIp(ip, userInfo.Id); errCode != nil { + SendJsonResponse(c, errCode, nil) + return + } + + } + // TODO STEP3 密码强弱提醒 + // 密码强弱提醒 + if config.ForceUpdateState == 1 && config.MinPwdLevel > userInfo.PwdLevel { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("密码强度弱,请联系管理员修改密码!")), nil) + return + } + // TODO STEP4 验证码正确性校验 + exist, err := loginInf.VerifyPhoneCode(req.Phone) + if err != nil { + SendJsonResponse(c, resp.FAIL.ErrorDetail(err), nil) + return + } + if exist != 1 { + SendJsonResponse(c, resp.FAIL.ErrorDetail(errors.New("验证码无效")), nil) + return + } + + lastLogin, msg, uuidStr, err := loginInf.UserPhoneLogin(req, userInfo) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + c.SetCookie(conf.CookieName, uuidStr, 1*60*60*24, "/", "", false, false) + c.SetCookie(conf.CookieNameLastLogin, lastLogin, 1*60*60*24, "/", "", false, false) + header.AddUserLoginLog("登入", "phone/login", constant.OpTypeIntMap[constant.Login], c, userInfo) + + SendJsonResponse(c, resp.OK, msg) +} + +// 根据手机号获取账户 +func PhoneToAccount(c *gin.Context) { + phone := c.Query("phone") + if !util.CheckMobile(phone) { + SendJsonResponse(c, errors.New("手机号不合法!"), nil) + return + } + loginInf := service.UserSvc{} + data, err := loginInf.PhoneToAccount(phone) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonResponse(c, resp.OK, data) +} + +// 验证码校验 +func ForgetPwdCheck(c *gin.Context) { + req := request.ForgetPwdCheck{} + err := c.BindJSON(&req) + if err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), nil) + return + } + if err := vd.Validate(req); err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), "") + return + } + loginInf := service.UserSvc{} + err = loginInf.ForgetPwdCheck(req) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonResponse(c, resp.OK, nil) +} + +// 忘记密码 +func UpdateAccountPwd(c *gin.Context) { + req := request.AccountForgetPasswordReq{} + err := c.BindJSON(&req) + if err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), nil) + return + } + if err := vd.Validate(req); err != nil { + SendJsonResponse(c, resp.InvalidParam.ErrorDetail(err), "") + return + } + loginInf := service.UserSvc{} + err = loginInf.UpdateAccountPwd(req) + if err != nil { + SendJsonResponse(c, err, nil) + return + } + SendJsonResponse(c, resp.OK, nil) +} diff --git a/src/main.go b/src/main.go index 0935885..16830ea 100644 --- a/src/main.go +++ b/src/main.go @@ -79,6 +79,10 @@ func initConfig() { LocationKey: util.SetEnvStr("LOCATION_KEY", "QKFBZ-PGGWJ-VZQFF-FHPA7-QWT5H-YHF4T"), PrivateKeySSH: util.SetEnvStr("PRIVATE_KEY_SSH", "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAIEAsOFk9OUB8wg9fd+PDHyX8nEtTSPSZY+tjxq2da1Pf5FkIn+U1da6\nh2eqowF9lnyvlt7uEledTIWQZDGWToGYCZnRommSZEpo/vII+l1P28bJVHfgWFCqmxNfIB\nZFQ4KrOp9rXKidmrd8flhK/NTLJNqryrhhIiDs3CTyAliscIsAAAIQTuM2gU7jNoEAAAAH\nc3NoLXJzYQAAAIEAsOFk9OUB8wg9fd+PDHyX8nEtTSPSZY+tjxq2da1Pf5FkIn+U1da6h2\neqowF9lnyvlt7uEledTIWQZDGWToGYCZnRommSZEpo/vII+l1P28bJVHfgWFCqmxNfIBZF\nQ4KrOp9rXKidmrd8flhK/NTLJNqryrhhIiDs3CTyAliscIsAAAADAQABAAAAgDjcfGPtqq\n7CG2J3l7jf5MjfcTy3I0/a3GSApd82k7PivVoJwYLswJH+1XAJbqIN+zR4/fePitWqqjxL\nZJJgPstuXpBZuJDvGwMqfl7wHRL2Qx34sRG02hG5e3uIfMxe5lHcPba0qsVQt+vOhu9MUb\nsYF/mfuQJKt/Oi8nA1BbrBAAAAQFQPrap7AtYWEoCIY7gtpFMW51iDTAv5GN99DsKNuBby\nwQX2S0Wg/da75m/emJn/2IbmaKApvrx8LbenpyywfBkAAABBAN6xiYQ2j7eRjLV4h4Hbie\nVwlPYP4otKHdF5meObr+2ifYiMktdv/44V1XWKhgavjGFNWx2sHgj7byb51e/bi3MAAABB\nAMtVxa55G0wS9Yw1WK2F4JdYZ65ZAnUuo2rbA2dMDQxsOQxgel5Ox2XmC7e0GKrO9BJKPo\nR2fHEOdm9KOmoB8IkAAAAWY2hlbnppbG9uZ0BleGFtcGxlLmNvbQECAwQF\n-----END OPENSSH PRIVATE KEY-----\n"), PublicKeySSH: util.SetEnvStr("PUBLIC_KEY_SSH", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCw4WT05QHzCD19348MfJfycS1NI9Jlj62PGrZ1rU9/kWQif5TV1rqHZ6qjAX2WfK+W3u4SV51MhZBkMZZOgZgJmdGiaZJkSmj+8gj6XU/bxslUd+BYUKqbE18gFkVDgqs6n2tcqJ2at3x+WEr81Msk2qvKuGEiIOzcJPICWKxwiw== chenzilong@example.com\n"), + SmsAccessKeyId: util.SetEnvStr("SMS_ACCESS_KEY", "LTAI4GBcVubRjzX7ABPcHnhB"), // 短信key + SmsAccessSecret: util.SetEnvStr("SMS_ACCESS_SECRET", "dYE2dtABFOqYtK1ijcrits0yedHkw7"), // 短信secret + SmsTemplateLogin: util.SetEnvStr("SMS_TEMPLATE_LOGIN", "SMS_212925130"), // 短信验证码模板 + SmsSignName: util.SetEnvStr("SMS_SIGN_NAME", "比格数据"), // 签名 } } diff --git a/src/router/loginrouter.go b/src/router/loginrouter.go index c35d9cb..1fb62ba 100644 --- a/src/router/loginrouter.go +++ b/src/router/loginrouter.go @@ -11,10 +11,15 @@ import ( func InitSystemLoginRouter(e *gin.Engine) { base := e.Group(fmt.Sprintf("%s/user", conf.Options.Prefix)) { - base.POST("/login", controller.UserLogin) // 登录 - base.POST("/logout", controller.UserLogout) // 登出 - base.GET("/getUserInfo", controller.GetUserInfo) // 获取用户信息 - base.GET("/getCaptcha", controller.GetCaptcha) // 获取验证码 - base.GET("/verifyCaptcha", controller.VerifyCaptcha) // 校验验证码 + base.POST("/login", controller.UserLoginV2) // 登录 + base.POST("/logout", controller.UserLogout) // 登出 + base.GET("/getUserInfo", controller.GetUserInfo) // 获取用户信息 + base.GET("/getCaptcha", controller.GetCaptcha) // 获取验证码 + base.GET("/verifyCaptcha", controller.VerifyCaptcha) // 校验验证码 + base.POST("/sms/verifyCode", controller.GetSmsVerifyCode) // 获取短信验证码(5分钟过期) + base.POST("/phone/login", controller.UserPhoneLogin) // 手机号登录 + base.GET("/phoneToAccount", controller.PhoneToAccount) // 根据手机号获取账户 + base.POST("/forget/pwd/check", controller.ForgetPwdCheck) // 忘记密码校验 + base.POST("/update/pwd", controller.UpdateAccountPwd) // 忘记密码 } } diff --git a/src/service/login.go b/src/service/login.go index 9f129be..1402303 100644 --- a/src/service/login.go +++ b/src/service/login.go @@ -6,13 +6,16 @@ import ( "errors" "fmt" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/tools" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "strconv" + dysmsapi "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" "image/color" "strings" "time" @@ -37,11 +40,9 @@ func (u *UserSvc) GetUserInfo(phone string) (userInfo entity.SystemUserInfo, err if err != nil { return userInfo, resp.DbConnectError.ErrorDetail(err) } - var userRole []entity.SystemUserInfo + // 查询用户 userModel := db.Table("system_user") - userModel.Join("LEFT", []string{"system_user_role", "sur"}, "sur.user_id = system_user.id") - userModel.Join("LEFT", []string{"system_role", "sr"}, "sur.role_id = sr.role_id") userModel.Join("LEFT", []string{"system_organization", "so"}, "so.organization_id = system_user.organization_id") userModel.Select("system_user.*,so.name as org_name") if phone == "" { @@ -49,15 +50,11 @@ func (u *UserSvc) GetUserInfo(phone string) (userInfo entity.SystemUserInfo, err } else { userModel.Where("system_user.phone = ?", phone) } - err = userModel.Where("system_user.state = 1").Find(&userRole) + _, err = userModel.Where("system_user.state = 1").Get(&userInfo) if err != nil { return userInfo, resp.DbSelectError.ErrorDetail(err) } - if len(userRole) == 0 && phone == "" { - return userInfo, resp.FAIL.WithError(errors.New("该账户未启用,不能登录")) - } else if len(userRole) == 0 && phone != "" { - return userInfo, resp.FAIL.WithError(errors.New("手机号未注册!")) - } + return userInfo, nil } @@ -356,15 +353,15 @@ func (u *UserSvc) LoginV2(userInfo entity.SystemUserInfo) (last_login_time, msg } msg = "登录成功" conf.Logger.Info("登录成功", zap.String("msg", msg)) - //go func() { - // // 最后心跳时间 - // finalHeartBeatMap := map[string]interface{}{ - // "final_heartbeat_unix": time.Now().UnixNano() / 1e6, - // "final_heartbeat_flag": false, - // } - // finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) - // redisCli.HSet(conf.FinalHeartBeatUnixKey, userInfo.Id, string(finalHeartBeatByte)) - //}() + go func() { + // 最后心跳时间 + finalHeartBeatMap := map[string]interface{}{ + "final_heartbeat_unix": time.Now().UnixNano() / 1e6, + "final_heartbeat_flag": false, + } + finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) + redisCli.HSet(conf.FinalHeartBeatUnixKey, strconv.Itoa(userInfo.Id), string(finalHeartBeatByte)) + }() return } @@ -419,3 +416,167 @@ func (u *UserSvc) SaveUserInfo(userInfo entity.SystemUserInfo, r client.Redis) ( } return last_login, uuidStr, nil } + +// 获取短信验证码 +func (u *UserSvc) GetSmsVerifyCode(phone string) (data interface{}, err error) { + smsClient, err := dysmsapi.NewClientWithAccessKey("cn-hangzhou", conf.Options.SmsAccessKeyId, conf.Options.SmsAccessSecret) + if err != nil { + conf.Logger.Error("dysmsapi client error", zap.Error(err)) + return nil, resp.FAIL.ErrorDetail(err) + } + code := util.Rand6() + params := map[string]interface{}{"code": code} + templateParam, err := json.Marshal(params) + if err != nil { + conf.Logger.Error("序列化模板失败!", zap.Error(err)) + return nil, resp.FAIL.ErrorDetail(err) + } + request := dysmsapi.CreateSendSmsRequest() + request.Scheme = "https" + request.PhoneNumbers = phone + request.TemplateCode = conf.Options.SmsTemplateLogin + request.SignName = conf.Options.SmsSignName + request.TemplateParam = string(templateParam) + req, err := smsClient.SendSms(request) + if err != nil { + return nil, resp.FAIL.ErrorDetail(err) + } + data, err = u.SetCodeToRedis(phone, code) + if err != nil { + conf.Logger.Error("set to cache is error!", zap.Error(err)) + return nil, resp.FAIL.ErrorDetail(err) + } + fmt.Printf("response is %#v\n", req) + return data, nil +} + +// 验证码保存 +func (u *UserSvc) SetCodeToRedis(phone, code string) (data interface{}, err error) { + rcon, err := client.GetRedisClient() + if err != nil { + return nil, resp.RedisConnectError.ErrorDetail(err) + } + err = rcon.Set(phone, code, time.Minute*5) + if err != nil { + return nil, resp.DbInsertError.ErrorDetail(err) + } + str, err := rcon.Get(phone) + if err != nil { + return nil, resp.RedisExecError.ErrorDetail(err) + } + return str, nil +} + +// 校验验证码是否过期 +func (u *UserSvc) VerifyLoginCodeExpire(phone, code string, redisCli client.Redis) error { + str, err := redisCli.Get(phone) + if err != nil { + conf.Logger.Error("code不存在!") + } + if str != code { + return errors.New("验证码无效!") + } + return nil +} + +func (u *UserSvc) VerifyPhoneCode(phone string) (exist int64, err error) { + rcon, err := client.GetRedisClient() + if err != nil { + return 0, err + } + exist, err = rcon.Exist(phone) + if err != nil { + return 0, err + } + return exist, nil +} + +// 手机号登录 +func (u *UserSvc) UserPhoneLogin(req request.SendPhoneMsg, userInfo entity.SystemUserInfo) (last_login_time, msg string, uuidStr string, err error) { + redisCli, err := client.GetRedisClient() + if err != nil { + return "", "", "", resp.RedisConnectError.ErrorDetail(err) + } + // TODO 用户数据、登录时间存入redis + last_login_time, uuidStr, err = u.SaveUserInfo(userInfo, redisCli) + if err != nil { + conf.Logger.Error("保存用户数据失败", zap.Error(err)) + return "", "", "", resp.RedisExecError.ErrorDetail(err) + } + msg = "登录成功" + conf.Logger.Info("登录成功", zap.String("msg", msg)) + // 登录成功,清除验证码 + go func() { + // 最后心跳时间 + finalHeartBeatMap := map[string]interface{}{ + "final_heartbeat_unix": time.Now().UnixNano() / 1e6, + "final_heartbeat_flag": false, + } + finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) + redisCli.HSet(conf.FinalHeartBeatUnixKey, strconv.Itoa(userInfo.Id), string(finalHeartBeatByte)) + redisCli.Del(req.Phone) + }() + return +} + +// 根据手机号获取账户 +func (u *UserSvc) PhoneToAccount(phone string) (data string, err error) { + db, err := client.GetDbClient() + if err != nil { + err = resp.DbConnectError.ErrorDetail(err) + return + } + systemUser := entity.SystemUser{} + _, err = db.Table("system_user").Select("system_account").Where("phone = ?", phone).Get(&systemUser) + if err != nil { + conf.Logger.Error("获取账号信息失败", zap.Error(err)) + return "", resp.DbSelectError.ErrorDetail(err) + } + return systemUser.SystemAccount, nil +} + +func (u *UserSvc) ForgetPwdCheck(req request.ForgetPwdCheck) (err error) { + rcon, err := client.GetRedisClient() + if err != nil { + return resp.RedisConnectError.ErrorDetail(err) + } + str, err := rcon.Get(req.Phone) + if err != nil { + return resp.RedisExecError.ErrorDetail(err) + } + if str != req.Code { + return resp.FAIL.ErrorDetail(errors.New("验证码不正确")) + } + return +} + +func (u *UserSvc) UpdateAccountPwd(req request.AccountForgetPasswordReq) (err error) { + db, err := client.GetDbClient() + if err != nil { + err = resp.DbConnectError.ErrorDetail(err) + return + } + oldSystemUser := entity.SystemUser{} + has, err := db.Cols("password", "id").Where("phone = ?", req.Phone).Get(&oldSystemUser) + if err != nil || !has { + conf.Logger.Error("查询系统账户详情失败", zap.Error(err)) + err = resp.DbSelectError.ErrorDetail(errors.New("查询系统账户详情失败")) + return + } + + password, err := SolvePassword(oldSystemUser.Id, req.Password) + if err != nil { + return + } + SystemUser := entity.SystemUser{} + SystemUser.Password = password + SystemUser.PwdForceStatus = 0 + SystemUser.PwdUpdatedTime = time.Now() + _, err = db.Where("id =?", oldSystemUser.Id).Where("is_deleted = 0").Cols("password", "pwd_force_status", "pwd_updated_time").Update(&SystemUser) + if err != nil { + conf.Logger.Error("修改系统账户失败", zap.Error(err)) + err = resp.DbUpdateError.ErrorDetail(errors.New("修改系统账户失败")) + return + } + return +} diff --git a/src/util/common.go b/src/util/common.go index cdea601..3151996 100644 --- a/src/util/common.go +++ b/src/util/common.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "math/rand" "net/http" "regexp" "strings" @@ -151,3 +152,15 @@ func SubDays(t1, t2 time.Time) (day int) { } return } + +// CheckMobile 检验手机号 +func CheckMobile(phone string) bool { + regRuler := "^1[345789]{1}\\d{9}$" + reg := regexp.MustCompile(regRuler) + return reg.MatchString(phone) +} + +// Rand6 生成一个6位随机数字 +func Rand6() string { + return fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)) +} -- 2.26.0