From 76ae5287ebbd87004c7a990ee5e6f7dbb7580d75 Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 17 Jul 2025 11:40:08 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A3=9E=E4=B9=A6?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- init.go | 1 + login_driver/feishu/client.go | 56 +++++++++++++ login_driver/feishu/entity.go | 27 +++++++ login_driver/feishu/feishu.go | 144 ++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 login_driver/feishu/client.go create mode 100644 login_driver/feishu/entity.go create mode 100644 login_driver/feishu/feishu.go diff --git a/go.mod b/go.mod index a0e81c20..80fa7b41 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( // github.com/eolinker/eosc => ../../eolinker/eosc //) -//replace github.com/eolinker/ap-account => ../../eolinker/ap-account +replace github.com/eolinker/ap-account => ../../eolinker/ap-account // //replace github.com/eolinker/go-common => ../../eolinker/go-common diff --git a/init.go b/init.go index af7b6ca9..4b0a0c86 100644 --- a/init.go +++ b/init.go @@ -4,6 +4,7 @@ package main import ( _ "github.com/APIParkLab/APIPark/frontend" _ "github.com/APIParkLab/APIPark/gateway/apinto" + _ "github.com/APIParkLab/APIPark/login_driver/feishu" _ "github.com/APIParkLab/APIPark/plugins/core" _ "github.com/APIParkLab/APIPark/plugins/openapi" _ "github.com/APIParkLab/APIPark/plugins/permit" diff --git a/login_driver/feishu/client.go b/login_driver/feishu/client.go new file mode 100644 index 00000000..fce3727e --- /dev/null +++ b/login_driver/feishu/client.go @@ -0,0 +1,56 @@ +package feishu + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +var ( + client = http.Client{ + Timeout: 10 * time.Second, + } +) + +func SendRequest[T any](uri string, method string, header http.Header, query url.Values, body []byte) (*T, error) { + if uri == "" { + return nil, fmt.Errorf("invalid URL") + } + + req, err := http.NewRequest(method, uri, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + if query != nil { + req.URL.RawQuery = query.Encode() + } + + if header != nil { + req.Header = header + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + result := new(T) + err = json.Unmarshal(respBody, result) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("status code error: %d, response: %s", resp.StatusCode, respBody) + } + + return result, nil +} diff --git a/login_driver/feishu/entity.go b/login_driver/feishu/entity.go new file mode 100644 index 00000000..c8a93b04 --- /dev/null +++ b/login_driver/feishu/entity.go @@ -0,0 +1,27 @@ +package feishu + +type UserTokenResponse struct { + Code int `json:"code"` + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + RefreshToken string `json:"refresh_token"` + RefreshTokenExpiresIn int `json:"refresh_token_expires_in"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} + +type UserInfoResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data UserInfo `json:"data"` +} + +type UserInfo struct { + Name string `json:"name"` + OpenID string `json:"open_id"` + UnionId string `json:"union_id"` + Email string `json:"email"` + Mobile string `json:"mobile"` +} diff --git a/login_driver/feishu/feishu.go b/login_driver/feishu/feishu.go new file mode 100644 index 00000000..a5337999 --- /dev/null +++ b/login_driver/feishu/feishu.go @@ -0,0 +1,144 @@ +package feishu + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + + "github.com/eolinker/eosc/common/bean" + + "github.com/eolinker/ap-account/service/user" + + "github.com/eolinker/go-common/utils" + + "github.com/google/uuid" + + "gorm.io/gorm" + + "github.com/eolinker/ap-account/service/account" + + "github.com/eolinker/ap-account/auth_driver" +) + +const ( + name = "feishu" + title = "飞书" + getTokenUri = "https://open.feishu.cn/open-apis/authen/v2/oauth/token" + getUserInfoUri = "https://open.feishu.cn/open-apis/authen/v1/user_info" +) + +var _ auth_driver.IDriver = (*Driver)(nil) + +func init() { + d := &Driver{} + bean.Autowired(&d.accountService) + bean.Autowired(&d.userService) + auth_driver.Register(name, d) +} + +type Driver struct { + accountService account.IAccountService `autowired:""` + userService user.IUserService `autowired:""` +} + +func (d *Driver) FilterConfig(config map[string]string) { + delete(config, "client_secret") +} + +func (d *Driver) Name() string { + return name +} + +func (d *Driver) Title() string { + return title +} + +func (d *Driver) ThirdLogin(ctx context.Context, args map[string]string) (string, error) { + code, ok := args["code"] + if !ok { + return "", fmt.Errorf("missing code parameter") + } + clientId, ok := args["client_id"] + if !ok { + return "", fmt.Errorf("missing client_id parameter") + } + clientSecret, ok := args["client_secret"] + if !ok { + return "", fmt.Errorf("missing client_secret parameter") + } + tokenResp, err := getUserToken(code, clientId, clientSecret) + if err != nil { + return "", err + } + userInfoResp, err := getUserInfo(tokenResp.TokenType, tokenResp.AccessToken) + if err != nil { + return "", err + } + userId := userInfoResp.Data.UnionId + username := userInfoResp.Data.Name + email := userInfoResp.Data.Email + mobile := userInfoResp.Data.Mobile + info, err := d.accountService.GetIdentifier(ctx, name, userId) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return "", err + } + uId := uuid.NewString() + err = d.accountService.Save(ctx, name, uId, userId, utils.Md5(fmt.Sprintf("%s%s", uId, userId))) + if err != nil { + return "", err + } + _, err = d.userService.Create(ctx, uId, username, email, mobile, "") + if err != nil { + return "", err + } + return userId, nil + } + _, err = d.userService.Update(ctx, info.Uid, &username, &email, &mobile) + if err != nil { + return "", err + } + + return userId, nil +} + +func getUserToken(code string, clientId string, clientSecret string) (*UserTokenResponse, error) { + headers := http.Header{} + headers.Set("Content-Type", "application/json") + body := url.Values{} + body.Set("grant_type", "authorization_code") + body.Set("code", code) + body.Set("client_id", clientId) + body.Set("client_secret", clientSecret) + resp, err := SendRequest[UserTokenResponse](getTokenUri, http.MethodPost, headers, nil, []byte(body.Encode())) + if err != nil { + return nil, fmt.Errorf("failed to get user token: %w", err) + } + if resp.Code != 0 { + return nil, fmt.Errorf("failed to get user token: %s", resp.ErrorDescription) + } + return resp, nil +} + +func getUserInfo(tokenType string, token string) (*UserInfoResponse, error) { + headers := http.Header{} + headers.Set("Content-Type", "application/json") + switch tokenType { + case "Bearer": + headers.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + } + resp, err := SendRequest[UserInfoResponse](getUserInfoUri, http.MethodGet, headers, nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to get user info: %w", err) + } + if resp.Code != 0 { + return nil, fmt.Errorf("failed to get user info: %s", resp.Msg) + } + return resp, nil +} + +func (d *Driver) Delete(ctx context.Context, ids ...string) error { + return d.accountService.OnRemoveUsers(ctx, ids...) +} From 2f314e0e6404f05a1a52e5abc0a92672d2a7dfcc Mon Sep 17 00:00:00 2001 From: Liujian <824010343@qq.com> Date: Thu, 17 Jul 2025 12:12:27 +0800 Subject: [PATCH 2/2] support feishu login --- go.mod | 4 ++-- go.sum | 4 ++-- resources/access/access.yaml | 10 ++++++++++ resources/access/role.yaml | 4 ++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 80fa7b41..f5c8d863 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.4 toolchain go1.23.6 require ( - github.com/eolinker/ap-account v1.0.15 + github.com/eolinker/ap-account v1.0.16 github.com/eolinker/eosc v0.18.3 github.com/eolinker/go-common v1.1.7 github.com/gabriel-vasile/mimetype v1.4.4 @@ -86,7 +86,7 @@ require ( // github.com/eolinker/eosc => ../../eolinker/eosc //) -replace github.com/eolinker/ap-account => ../../eolinker/ap-account +//replace github.com/eolinker/ap-account => ../../eolinker/ap-account // //replace github.com/eolinker/go-common => ../../eolinker/go-common diff --git a/go.sum b/go.sum index 431be974..2ff0d7ee 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eolinker/ap-account v1.0.15 h1:n6DJeL6RHZ8eLlZUcY2U3H4d/GPaA5oelAx3R0E6yL8= -github.com/eolinker/ap-account v1.0.15/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g= +github.com/eolinker/ap-account v1.0.16 h1:v1VvSeQ2AvxAvkYT4n4APqZdWS8d1CbA/1O0LYEyNM4= +github.com/eolinker/ap-account v1.0.16/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g= github.com/eolinker/eosc v0.18.3 h1:3IK5HkAPnJRfLbQ0FR7kWsZr6Y/OiqqGazvN1q2BL5A= github.com/eolinker/eosc v0.18.3/go.mod h1:O9PQQXFCpB6fjHf+oFt/LN6EOAv779ItbMixMKCfTfk= github.com/eolinker/go-common v1.1.7 h1:bi7wDmlCYQGjS3k8Bz/o+Mo9aMJAzmPsBLXWurxPfwk= diff --git a/resources/access/access.yaml b/resources/access/access.yaml index bafec116..4beef30e 100644 --- a/resources/access/access.yaml +++ b/resources/access/access.yaml @@ -131,6 +131,16 @@ system: value: 'manager' dependents: - system.settings.ai_api.view + - name: login + value: 'login' + children: + - name: view + value: 'view' + guest_allow: true + - name: manager + value: 'manager' + dependents: + - system.settings.login.view - name: ai log value: 'ai_log' children: diff --git a/resources/access/role.yaml b/resources/access/role.yaml index 6637cbae..30714d52 100644 --- a/resources/access/role.yaml +++ b/resources/access/role.yaml @@ -26,6 +26,8 @@ system: - system.settings.general.view - system.settings.log_configuration.manager - system.settings.log_configuration.view + - system.settings.login.manager + - system.settings.login.view - system.settings.mcp.view - system.settings.mcp.manager - system.settings.role.view @@ -69,6 +71,8 @@ system: - system.settings.general.view - system.settings.log_configuration.manager - system.settings.log_configuration.view + - system.settings.login.manager + - system.settings.login.view - system.settings.ssl_certificate.manager - system.settings.ssl_certificate.view - system.settings.strategy.view