From 513f8ea012f8da275b70fd55f5ebebe171e95ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Fri, 21 Jul 2023 21:18:31 +0800 Subject: [PATCH] =?UTF-8?q?wip:=20oauth2=E7=99=BB=E5=BD=95=E5=92=8Coauth2?= =?UTF-8?q?=20otp=E7=99=BB=E5=BD=95=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mayfly_go_web/src/common/openApi.ts | 1 + .../views/login/component/AccountLogin.vue | 76 +++---- mayfly_go_web/src/views/login/index.vue | 56 ++++- mayfly_go_web/src/views/personal/index.vue | 30 ++- .../src/views/system/auth/AuthInfo.vue | 22 +- .../src/views/system/config/ConfigList.vue | 7 +- server/internal/sys/api/account.go | 75 ++++--- server/internal/sys/api/auth.go | 197 +++++++++++++++++- server/internal/sys/api/config.go | 34 +++ server/internal/sys/api/form/auth.go | 1 + server/internal/sys/api/vo/auth.go | 5 + .../internal/sys/application/application.go | 5 + server/internal/sys/application/auth.go | 29 +++ server/internal/sys/domain/entity/auth.go | 17 ++ server/internal/sys/domain/entity/config.go | 1 + server/internal/sys/domain/repository/auth.go | 10 + .../sys/infrastructure/persistence/auth.go | 24 +++ .../infrastructure/persistence/persistence.go | 15 +- server/internal/sys/router/auth.go | 10 +- server/internal/sys/router/config.go | 13 +- server/migrations/20230720.go | 2 +- 21 files changed, 507 insertions(+), 123 deletions(-) create mode 100644 server/internal/sys/application/auth.go create mode 100644 server/internal/sys/domain/entity/auth.go create mode 100644 server/internal/sys/domain/repository/auth.go create mode 100644 server/internal/sys/infrastructure/persistence/auth.go diff --git a/mayfly_go_web/src/common/openApi.ts b/mayfly_go_web/src/common/openApi.ts index d7404881..062d8ada 100644 --- a/mayfly_go_web/src/common/openApi.ts +++ b/mayfly_go_web/src/common/openApi.ts @@ -6,6 +6,7 @@ export default { changePwd: (param: any) => request.post('/sys/accounts/change-pwd', param), getPublicKey: () => request.get('/common/public-key'), getConfigValue: (params: any) => request.get('/sys/configs/value', params), + oauthConfig: () => request.get('/sys/configs/auth'), captcha: () => request.get('/sys/captcha'), logout: () => request.post('/sys/accounts/logout/{token}'), getPermissions: () => request.get('/sys/accounts/permissions'), diff --git a/mayfly_go_web/src/views/login/component/AccountLogin.vue b/mayfly_go_web/src/views/login/component/AccountLogin.vue index e72bdaf4..5f7ff958 100644 --- a/mayfly_go_web/src/views/login/component/AccountLogin.vue +++ b/mayfly_go_web/src/views/login/component/AccountLogin.vue @@ -2,37 +2,24 @@
- + - + - + @@ -73,28 +58,17 @@ - - + + - + @@ -265,10 +239,14 @@ const onSignIn = async () => { return; } state.showLoginFailTips = false; + loginResDeal(loginRes); +} + +const loginResDeal = (loginRes: any) => { // 用户信息 const userInfos = { name: loginRes.name, - username: state.loginForm.username, + username: loginRes.username, // 头像 photo: letterAvatar(state.loginForm.username), time: new Date().getTime(), @@ -297,6 +275,10 @@ const onSignIn = async () => { }, 400); }; +defineExpose({ + loginResDeal +}); + // 登录成功后的跳转 const signInSuccess = async (accessToken: string = '') => { // 存储 token 到浏览器缓存 diff --git a/mayfly_go_web/src/views/login/index.vue b/mayfly_go_web/src/views/login/index.vue index 0f337dc5..36e83c0c 100644 --- a/mayfly_go_web/src/views/login/index.vue +++ b/mayfly_go_web/src/views/login/index.vue @@ -9,7 +9,7 @@ - + - + +
+
+ + + + + + + +
@@ -160,18 +154,18 @@ 立即设置 - + -->
-
绑定QQ
-
已绑定QQ:110****566
+
绑定OAuth2
+
已绑定OAuth2
- 立即设置 + 立即绑定
-
--> + diff --git a/mayfly_go_web/src/views/system/auth/AuthInfo.vue b/mayfly_go_web/src/views/system/auth/AuthInfo.vue index a37b6db5..39fe8451 100644 --- a/mayfly_go_web/src/views/system/auth/AuthInfo.vue +++ b/mayfly_go_web/src/views/system/auth/AuthInfo.vue @@ -22,22 +22,28 @@ + placeholder="授权码获取地址 例如: https://example.com/oauth/authorize"> - + - + - + - + - + + + + 保存 @@ -50,7 +56,7 @@ " + + "") +} + +func (a *Auth) getOAuthClient() (*oauth2.Config, *vo.OAuth2VO, error) { + config := a.ConfigApp.GetConfig(AuthOAuth2Key) + oauth2Vo := &vo.OAuth2VO{} + if config.Value != "" { + if err := json.Unmarshal([]byte(config.Value), oauth2Vo); err != nil { + global.Log.Warnf("解析自定义oauth2配置失败,err:%s", err.Error()) + return nil, nil, errors.New("解析自定义oauth2配置失败") + } + } + if oauth2Vo.ClientID == "" { + biz.ErrIsNil(nil, "请先配置oauth2") + return nil, nil, errors.New("请先配置oauth2") + } + client := &oauth2.Config{ + ClientID: oauth2Vo.ClientID, + ClientSecret: oauth2Vo.ClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: oauth2Vo.AuthorizationURL, + TokenURL: oauth2Vo.AccessTokenURL, + }, + RedirectURL: oauth2Vo.RedirectURL + "api/sys/auth/oauth2/callback", + Scopes: strings.Split(oauth2Vo.Scopes, ","), + } + return client, oauth2Vo, nil } // GetInfo 获取认证平台信息 diff --git a/server/internal/sys/api/config.go b/server/internal/sys/api/config.go index 2d59d450..85cfdd47 100644 --- a/server/internal/sys/api/config.go +++ b/server/internal/sys/api/config.go @@ -1,11 +1,14 @@ package api import ( + "encoding/json" "mayfly-go/internal/sys/api/form" + "mayfly-go/internal/sys/api/vo" "mayfly-go/internal/sys/application" "mayfly-go/internal/sys/domain/entity" "mayfly-go/pkg/biz" "mayfly-go/pkg/ginx" + "mayfly-go/pkg/global" "mayfly-go/pkg/req" ) @@ -25,6 +28,21 @@ func (c *Config) GetConfigValueByKey(rc *req.Ctx) { rc.ResData = c.ConfigApp.GetConfig(key).Value } +func (c *Config) GetConfigValueByKeyWithNoToken(keys []string) func(rc *req.Ctx) { + keyMap := make(map[string]struct{}) + for _, key := range keys { + keyMap[key] = struct{}{} + } + return func(rc *req.Ctx) { + key := rc.GinCtx.Query("key") + biz.NotEmpty(key, "key不能为空") + if _, ok := keyMap[key]; !ok { + biz.ErrIsNil(nil, "无权限获取该配置信息") + } + rc.ResData = c.ConfigApp.GetConfig(key).Value + } +} + func (c *Config) SaveConfig(rc *req.Ctx) { form := &form.ConfigForm{} config := ginx.BindJsonAndCopyTo(rc.GinCtx, form, new(entity.Config)) @@ -32,3 +50,19 @@ func (c *Config) SaveConfig(rc *req.Ctx) { config.SetBaseInfo(rc.LoginAccount) c.ConfigApp.Save(config) } + +// AuthConfig auth相关配置 +func (c *Config) AuthConfig(rc *req.Ctx) { + resp := &vo.OAuth2EnableVO{} + config := c.ConfigApp.GetConfig(AuthOAuth2Key) + oauth2 := &vo.OAuth2VO{} + if config.Value != "" { + if err := json.Unmarshal([]byte(config.Value), oauth2); err != nil { + global.Log.Warnf("解析自定义oauth2配置失败,err:%s", err.Error()) + biz.ErrIsNil(err, "解析自定义oauth2配置失败") + } else if oauth2.ClientID != "" { + resp.OAuth2 = true + } + } + rc.ResData = resp +} diff --git a/server/internal/sys/api/form/auth.go b/server/internal/sys/api/form/auth.go index eb6b4ddd..e75b9ced 100644 --- a/server/internal/sys/api/form/auth.go +++ b/server/internal/sys/api/form/auth.go @@ -9,4 +9,5 @@ type OAuth2Form struct { RedirectURL string `json:"redirectURL" binding:"required,url"` UserIdentifier string `json:"userIdentifier" binding:"required"` Scopes string `json:"scopes"` + AutoRegister bool `json:"autoRegister"` } diff --git a/server/internal/sys/api/vo/auth.go b/server/internal/sys/api/vo/auth.go index d22e929b..2356bda6 100644 --- a/server/internal/sys/api/vo/auth.go +++ b/server/internal/sys/api/vo/auth.go @@ -9,8 +9,13 @@ type OAuth2VO struct { RedirectURL string `json:"redirectURL"` UserIdentifier string `json:"userIdentifier"` Scopes string `json:"scopes"` + AutoRegister bool `json:"autoRegister"` } type AuthVO struct { *OAuth2VO `json:"oauth2"` } + +type OAuth2EnableVO struct { + OAuth2 bool `json:"oauth2"` +} diff --git a/server/internal/sys/application/application.go b/server/internal/sys/application/application.go index 08694abe..059aa04f 100644 --- a/server/internal/sys/application/application.go +++ b/server/internal/sys/application/application.go @@ -6,6 +6,7 @@ import ( var ( accountApp = newAccountApp(persistence.GetAccountRepo()) + authApp = newAuthApp(persistence.GetOAuthAccountRepo()) configApp = newConfigApp(persistence.GetConfigRepo()) resourceApp = newResourceApp(persistence.GetResourceRepo()) roleApp = newRoleApp(persistence.GetRoleRepo()) @@ -16,6 +17,10 @@ func GetAccountApp() Account { return accountApp } +func GetAuthApp() Auth { + return authApp +} + func GetConfigApp() Config { return configApp } diff --git a/server/internal/sys/application/auth.go b/server/internal/sys/application/auth.go new file mode 100644 index 00000000..a1772e64 --- /dev/null +++ b/server/internal/sys/application/auth.go @@ -0,0 +1,29 @@ +package application + +import ( + "mayfly-go/internal/sys/domain/entity" + "mayfly-go/internal/sys/domain/repository" +) + +type Auth interface { + GetOAuthAccount(condition *entity.OAuthAccount, cols ...string) error + BindOAuthAccount(e *entity.OAuthAccount) error +} + +func newAuthApp(oauthAccountRepo repository.OAuthAccount) Auth { + return &authAppImpl{ + oauthAccountRepo: oauthAccountRepo, + } +} + +type authAppImpl struct { + oauthAccountRepo repository.OAuthAccount +} + +func (a *authAppImpl) GetOAuthAccount(condition *entity.OAuthAccount, cols ...string) error { + return a.oauthAccountRepo.GetOAuthAccount(condition, cols...) +} + +func (a *authAppImpl) BindOAuthAccount(e *entity.OAuthAccount) error { + return a.oauthAccountRepo.SaveOAuthAccount(e) +} diff --git a/server/internal/sys/domain/entity/auth.go b/server/internal/sys/domain/entity/auth.go new file mode 100644 index 00000000..ead405ef --- /dev/null +++ b/server/internal/sys/domain/entity/auth.go @@ -0,0 +1,17 @@ +package entity + +import "time" + +type OAuthAccount struct { + Id uint64 `json:"id"` + + AccountId uint64 `json:"accountId" gorm:"column:account_id;index:account_id,unique"` + Identity string `json:"identity" gorm:"column:identity;index:identity,unique"` + + CreateTime *time.Time `json:"createTime"` + UpdateTime *time.Time `json:"updateTime"` +} + +func (OAuthAccount) TableName() string { + return "t_oauth_account" +} diff --git a/server/internal/sys/domain/entity/config.go b/server/internal/sys/domain/entity/config.go index 1e7a4a9e..e046cf99 100644 --- a/server/internal/sys/domain/entity/config.go +++ b/server/internal/sys/domain/entity/config.go @@ -12,6 +12,7 @@ const ( ConfigKeyUseLoginOtp string = "UseLoginOtp" // 是否开启otp双因素校验 ConfigKeyDbQueryMaxCount string = "DbQueryMaxCount" // 数据库查询的最大数量 ConfigKeyDbSaveQuerySQL string = "DbSaveQuerySQL" // 数据库是否记录查询相关sql + ConfigUseWartermark string = "UseWartermark" // 是否使用水印 ) type Config struct { diff --git a/server/internal/sys/domain/repository/auth.go b/server/internal/sys/domain/repository/auth.go new file mode 100644 index 00000000..e5605c56 --- /dev/null +++ b/server/internal/sys/domain/repository/auth.go @@ -0,0 +1,10 @@ +package repository + +import "mayfly-go/internal/sys/domain/entity" + +type OAuthAccount interface { + // GetOAuthAccount 根据identity获取第三方账号信息 + GetOAuthAccount(condition *entity.OAuthAccount, cols ...string) error + + SaveOAuthAccount(e *entity.OAuthAccount) error +} diff --git a/server/internal/sys/infrastructure/persistence/auth.go b/server/internal/sys/infrastructure/persistence/auth.go new file mode 100644 index 00000000..ead99ff9 --- /dev/null +++ b/server/internal/sys/infrastructure/persistence/auth.go @@ -0,0 +1,24 @@ +package persistence + +import ( + "mayfly-go/internal/sys/domain/entity" + "mayfly-go/internal/sys/domain/repository" + "mayfly-go/pkg/gormx" +) + +type authAccountRepoImpl struct{} + +func newAuthAccountRepo() repository.OAuthAccount { + return new(authAccountRepoImpl) +} + +func (a *authAccountRepoImpl) GetOAuthAccount(condition *entity.OAuthAccount, cols ...string) error { + return gormx.GetBy(condition, cols...) +} + +func (a *authAccountRepoImpl) SaveOAuthAccount(e *entity.OAuthAccount) error { + if e.Id == 0 { + return gormx.Insert(e) + } + return gormx.UpdateById(e) +} diff --git a/server/internal/sys/infrastructure/persistence/persistence.go b/server/internal/sys/infrastructure/persistence/persistence.go index ca95c5d2..ad07b943 100644 --- a/server/internal/sys/infrastructure/persistence/persistence.go +++ b/server/internal/sys/infrastructure/persistence/persistence.go @@ -3,17 +3,22 @@ package persistence import "mayfly-go/internal/sys/domain/repository" var ( - accountRepo = newAccountRepo() - configRepo = newConfigRepo() - resourceRepo = newResourceRepo() - roleRepo = newRoleRepo() - syslogRepo = newSyslogRepo() + accountRepo = newAccountRepo() + authAccountRepo = newAuthAccountRepo() + configRepo = newConfigRepo() + resourceRepo = newResourceRepo() + roleRepo = newRoleRepo() + syslogRepo = newSyslogRepo() ) func GetAccountRepo() repository.Account { return accountRepo } +func GetOAuthAccountRepo() repository.OAuthAccount { + return authAccountRepo +} + func GetConfigRepo() repository.Config { return configRepo } diff --git a/server/internal/sys/router/auth.go b/server/internal/sys/router/auth.go index 21066086..0935472b 100644 --- a/server/internal/sys/router/auth.go +++ b/server/internal/sys/router/auth.go @@ -2,6 +2,7 @@ package router import ( "github.com/gin-gonic/gin" + msgapp "mayfly-go/internal/msg/application" "mayfly-go/internal/sys/api" "mayfly-go/internal/sys/application" "mayfly-go/pkg/req" @@ -9,7 +10,10 @@ import ( func InitSysAuthRouter(router *gin.RouterGroup) { r := &api.Auth{ - ConfigApp: application.GetConfigApp(), + ConfigApp: application.GetConfigApp(), + AuthApp: application.GetAuthApp(), + AccountApp: application.GetAccountApp(), + MsgApp: msgapp.GetMsgApp(), } rg := router.Group("sys/auth") @@ -17,7 +21,11 @@ func InitSysAuthRouter(router *gin.RouterGroup) { reqs := [...]*req.Conf{ req.NewGet("", r.GetInfo).RequiredPermission(baseP), + req.NewPut("/oauth2", r.SaveOAuth2).RequiredPermission(baseP), + + req.NewGet("/oauth2/login", r.OAuth2Login).DontNeedToken(), + req.NewGet("/oauth2/callback", r.OAuth2Callback).NoRes().DontNeedToken(), } req.BatchSetGroup(rg, reqs[:]) diff --git a/server/internal/sys/router/config.go b/server/internal/sys/router/config.go index 75e2c683..e7511df2 100644 --- a/server/internal/sys/router/config.go +++ b/server/internal/sys/router/config.go @@ -3,6 +3,7 @@ package router import ( "mayfly-go/internal/sys/api" "mayfly-go/internal/sys/application" + "mayfly-go/internal/sys/domain/entity" "mayfly-go/pkg/req" "github.com/gin-gonic/gin" @@ -18,9 +19,17 @@ func InitSysConfigRouter(router *gin.RouterGroup) { req.NewGet("", r.Configs).RequiredPermission(baseP), // 获取指定配置key对应的值 - req.NewGet("/value", r.GetConfigValueByKey).RequiredPermission(baseP), + req.NewGet("/value", r.GetConfigValueByKeyWithNoToken([]string{ + entity.ConfigKeyAccountLoginSecurity, + entity.ConfigKeyDbQueryMaxCount, + entity.ConfigKeyDbSaveQuerySQL, + entity.ConfigUseWartermark, + })).DontNeedToken(), - req.NewPost("", r.SaveConfig).Log(req.NewLogSave("保存系统配置信息")).RequiredPermissionCode("config:save"), + req.NewGet("/auth", r.AuthConfig).DontNeedToken(), + + req.NewPost("", r.SaveConfig).Log(req.NewLogSave("保存系统配置信息")). + RequiredPermissionCode("config:save"), } req.BatchSetGroup(configG, reqs[:]) diff --git a/server/migrations/20230720.go b/server/migrations/20230720.go index 080e98d0..7d2208e9 100644 --- a/server/migrations/20230720.go +++ b/server/migrations/20230720.go @@ -84,7 +84,7 @@ func T20230720() *gormigrate.Migration { }).Error; err != nil { return err } - return nil + return tx.AutoMigrate(&entity.OAuthAccount{}) }, Rollback: func(tx *gorm.DB) error { return nil