kobe6258 8 月之前
父节点
当前提交
1bc6c5c1d8
共有 10 个文件被更改,包括 727 次插入15 次删除
  1. 79 0
      controllers/user_login.go
  2. 19 14
      go.mod
  3. 9 0
      routers/commentsRouter.go
  4. 167 0
      rpc/rpc.go
  5. 222 0
      rpc/sso/sso.pb.go
  6. 123 0
      rpc/sso/sso_grpc.pb.go
  7. 41 0
      rpc/sso/sso_rpc.go
  8. 9 0
      services/sso_service.go
  9. 8 1
      utils/config.go
  10. 50 0
      utils/grpc_client_pool.go

+ 79 - 0
controllers/user_login.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_mini_crm_ht/models"
 	"eta/eta_mini_crm_ht/models/request"
 	"eta/eta_mini_crm_ht/models/response"
+	"eta/eta_mini_crm_ht/services"
 	"eta/eta_mini_crm_ht/utils"
 	"fmt"
 	"time"
@@ -14,6 +15,84 @@ type UserLoginController struct {
 	BaseCommonController
 }
 
+// SSOLogin
+// @Title SSO用户登录
+// @Description SSO用户登录
+// @Param	request	body UserLoginReq true "type json string"
+// @Success 200 {object} models.LoginResp
+// @router /login [get]
+func (this *UserLoginController) SSOLogin() {
+
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	code := this.GetString("Code")
+	if code == "" {
+		br.Msg = "code不能为空"
+		return
+	}
+	token := services.SSOLogin(code)
+	////sysUser, err := models.GetSysUserBySysUserName(req.UserName)
+	////if err != nil {
+	////	if err.Error() == utils.ErrNoRow() {
+	////		br.Msg = "登录失败, 账号或密码错误"
+	////		return
+	////	} else {
+	////		br.Msg = "系统错误"
+	////		br.ErrMsg = "系统错误" + err.Error()
+	////		return
+	////	}
+	////}
+	//sysRole, err := models.GetSysRoleById(sysUser.SysRoleId)
+	//if err != nil {
+	//	br.Msg = "登录失败"
+	//	br.ErrMsg = "查询角色失败, Err:" + err.Error()
+	//	return
+	//}
+
+	//account := utils.MD5(sysUser.SysUserName)
+	//token := utils.GenToken(account)
+	//sysSession := new(models.SysSession)
+	//sysSession.UserName = sysUser.SysUserName
+	//sysSession.SysUserId = sysUser.SysUserId
+	//sysSession.ExpiredTime = time.Now().AddDate(0, 0, 60)
+	//sysSession.CreatedTime = time.Now()
+	//sysSession.LastUpdatedTime = time.Now()
+	//sysSession.AccessToken = token
+	//err = sysSession.AddSysSession()
+	//if err != nil {
+	//	br.Msg = "登录失败"
+	//	br.ErrMsg = "新增session信息失败, Err:" + err.Error()
+	//	return
+	//}
+	resp := new(response.LoginResp)
+	resp.Authorization = "authorization=" + token
+	//resp.SysUserName = sysUser.SysUserName
+	//resp.SysRealName = sysUser.SysRealName
+	//resp.SysUserId = sysUser.SysUserId
+	//resp.RoleName = sysRole.SysRoleName
+	//resp.RoleId = sysUser.SysRoleId
+
+	// 获取不可信的登录态,并将该登录态重置掉,不允许多次登录
+	//	noTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST, sysUser.SysUserId)
+	//noTrustLoginId, _ := utils.Rc.RedisString(noTrustLoginKey)
+	//fmt.Println("noTrustLoginId:", noTrustLoginId)
+	//if noTrustLoginId != `` { // 如果存在不可信设备,那么将其下架
+	//	oldNoTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, noTrustLoginId)
+	//	utils.Rc.Put(oldNoTrustLoginKey, "0", time.Hour*24)
+	//}
+	//// 设置redis缓存,记录用户登录态
+	//loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, sysSession.SysSessionId)
+	//utils.Rc.Put(loginKey, "1", time.Hour*24)
+	//utils.Rc.Put(noTrustLoginKey, sysSession.SysSessionId, time.Hour*24*60)
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "登录成功"
+}
+
 // Login
 // @Title 用户登录
 // @Description 用户登录

+ 19 - 14
go.mod

@@ -2,33 +2,38 @@ module eta/eta_mini_crm_ht
 
 go 1.21
 
-require github.com/beego/beego/v2 v2.1.0
+require github.com/beego/beego/v2 v2.3.0
 
 require (
 	github.com/aliyun/alibaba-cloud-sdk-go v1.62.807
 	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
 	github.com/beego/bee/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/go-kit/kit v0.12.1-0.20220826005032-a7ba4fa4e289
 	github.com/go-redis/redis/v8 v8.11.5
-	github.com/go-sql-driver/mysql v1.7.0
-	github.com/google/uuid v1.6.0
+	github.com/go-sql-driver/mysql v1.8.1
 	github.com/minio/minio-go/v7 v7.0.74
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/silenceper/wechat/v2 v2.1.6
 	github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300
+	google.golang.org/grpc v1.65.0
+	google.golang.org/protobuf v1.34.2
 )
 
 require (
+	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
-	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/fatih/structs v1.1.0 // indirect
 	github.com/go-ini/ini v1.67.0 // indirect
+	github.com/go-kit/log v0.2.1 // indirect
+	github.com/go-logfmt/logfmt v0.5.1 // indirect
 	github.com/goccy/go-json v0.10.3 // indirect
-	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
@@ -36,17 +41,16 @@ require (
 	github.com/klauspost/compress v1.17.9 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pkg/errors v0.9.1 // indirect
-	github.com/prometheus/client_golang v1.15.1 // indirect
-	github.com/prometheus/client_model v0.3.0 // indirect
-	github.com/prometheus/common v0.42.0 // indirect
-	github.com/prometheus/procfs v0.9.0 // indirect
+	github.com/prometheus/client_golang v1.19.0 // indirect
+	github.com/prometheus/client_model v0.5.0 // indirect
+	github.com/prometheus/common v0.48.0 // indirect
+	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/rs/xid v1.5.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 	github.com/sirupsen/logrus v1.9.0 // indirect
@@ -54,12 +58,13 @@ require (
 	github.com/tidwall/gjson v1.14.1 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
 	github.com/tidwall/pretty v1.2.0 // indirect
-	golang.org/x/crypto v0.24.0 // indirect
-	golang.org/x/net v0.26.0 // indirect
-	golang.org/x/sys v0.21.0 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	golang.org/x/crypto v0.26.0 // indirect
+	golang.org/x/net v0.28.0 // indirect
+	golang.org/x/sys v0.24.0 // indirect
 	golang.org/x/text v0.17.0 // indirect
 	golang.org/x/time v0.6.0 // indirect
-	google.golang.org/protobuf v1.30.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240820151423-278611b39280 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 9 - 0
routers/commentsRouter.go

@@ -621,6 +621,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "SSOLogin",
+            Router: `/login`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_mini_crm_ht/controllers:UserLoginController"],
         beego.ControllerComments{
             Method: "Login",

+ 167 - 0
rpc/rpc.go

@@ -0,0 +1,167 @@
+package rpc
+
+import (
+	"context"
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha256"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"encoding/pem"
+	"errors"
+	"eta/eta_mini_crm_ht/utils"
+	"fmt"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/anypb"
+	"io"
+	"log"
+	"time"
+)
+
+const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+type RpcService interface {
+	GetPool(addr string) utils.ClientPool
+}
+
+type DefaultRpcClient struct {
+}
+
+func (d *DefaultRpcClient) WrapSign(request interface{}, doHandler func(ctx context.Context, req interface{}) error) {
+	nonce, err := d.generateNonceStr(16)
+	if err != nil {
+		utils.FileLog.Error("生成随机串nonce失败:%v", err)
+		return
+	}
+	//时间戳
+	timestamp := time.Now().UnixMilli()
+	reqStr, err := json.Marshal(request)
+	if err != nil {
+		utils.FileLog.Error("序列化请求失败:%v", err)
+		return
+	}
+	sign, _ := d.signature(string(reqStr), nonce, timestamp)
+	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
+		"nonce", nonce,
+		"timestamp", fmt.Sprintf("%d", timestamp),
+		"signature", sign,
+	))
+	err = doHandler(ctx, request)
+	if err != nil {
+		utils.FileLog.Error("调用PRC服务失败:%v", err)
+	}
+}
+func (d *DefaultRpcClient) getOptions() (opts []grpc.DialOption) {
+	opts = make([]grpc.DialOption, 0)
+	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	return opts
+}
+
+func (d *DefaultRpcClient) GetPool(addr string) utils.ClientPool {
+	pool, err := utils.GetPoolInstance(addr, d.getOptions()...)
+	if err != nil {
+		log.Fatal(err)
+	}
+	return pool
+}
+
+// 生成签名
+func (d *DefaultRpcClient) generateSignature(req interface{}) string {
+	message := req.(proto.Message)
+	reqData, _ := anypb.New(message)
+	reqBytes, _ := proto.Marshal(reqData)
+
+	key := []byte("secret") // 秘钥应该保密
+	mac := hmac.New(sha256.New, key)
+	mac.Write(reqBytes)
+	return hex.EncodeToString(mac.Sum(nil))
+}
+
+func (d *DefaultRpcClient) generateNonceStr(length int) (string, error) {
+	result := make([]byte, length)
+	n, err := io.ReadFull(rand.Reader, result)
+	if err != nil {
+		return "", err
+	}
+	for i := 0; i < n; i++ {
+		result[i] = chars[int(result[i])%len(chars)]
+	}
+	return string(result), nil
+}
+
+func (d *DefaultRpcClient) signature(encryptData string, nonceStr string, timestamp int64) (sign string, err error) {
+	key := []byte("secret-hmac-key") // 秘钥应该保密
+	mac := hmac.New(sha256.New, key)
+	mac.Write([]byte(encryptData))
+	mac.Write([]byte(nonceStr))                     // 在计算签名时加入随机字符串
+	mac.Write([]byte(fmt.Sprintf("%d", timestamp))) // 在计算签名时加入时间戳
+	return hex.EncodeToString(mac.Sum(nil)), nil
+}
+
+type encryptedRequest struct {
+	Ciphertext []byte `json:"ciphertext"`
+	Nonce      string `json:"nonce"`     // 添加随机字符串
+	Timestamp  int64  `json:"timestamp"` // 添加时间戳
+}
+
+func (d *DefaultRpcClient) EncryptRequest(req interface{}, nonceStr string) (encodedData string, err error) {
+	message := req.(proto.Message)
+	reqData, _ := anypb.New(message)
+	reqBytes, _ := proto.Marshal(reqData)
+	// 使用公钥加密
+	var pubKey *rsa.PublicKey
+	pubKey, err = d.parseRSAPublicKeyFromPEM([]byte("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1234567890abcdefg==\n-----END PUBLIC KEY-----"))
+	if err != nil {
+		utils.FileLog.Error("公钥解析失败: %v", err)
+		return
+	}
+	var encryptedData []byte
+	// RSA加密
+	encryptedData, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, reqBytes, nil)
+	if err != nil {
+		utils.FileLog.Error("RSA加密失败: %v", err)
+		return
+	}
+
+	// 构建加密后的数据结构
+	encryptedStruct := encryptedRequest{
+		Ciphertext: encryptedData,
+		Nonce:      nonceStr,              // 添加随机字符串
+		Timestamp:  time.Now().UnixNano(), // 添加时间戳
+	}
+
+	// 序列化加密后的数据
+	encryptedData, err = json.Marshal(encryptedStruct)
+	if err != nil {
+		utils.FileLog.Error("序列化加密后的数据失败: %v", err)
+		return
+	}
+	// 编码加密后的数据
+	encodedData = base64.StdEncoding.EncodeToString(encryptedData)
+	return
+}
+
+// 解析RSA公钥
+func (d *DefaultRpcClient) parseRSAPublicKeyFromPEM(pemBytes []byte) (pubKey *rsa.PublicKey, err error) {
+	block, _ := pem.Decode(pemBytes)
+	if block == nil {
+		utils.FileLog.Error("公钥解析失败")
+		return nil, errors.New("公钥解析失败")
+	}
+	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+
+	pubKey, ok := pubInterface.(*rsa.PublicKey)
+	if !ok {
+		return nil, errors.New("RSA公钥格式错误")
+	}
+	return
+}

+ 222 - 0
rpc/sso/sso.pb.go

@@ -0,0 +1,222 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.28.0--rc2
+// source: rpc/protos/sso.proto
+
+//protoc --go_out=. --go-grpc_out=. rpc/protos/sso.proto
+
+package sso
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type LoginRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Code   string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
+	Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"`
+}
+
+func (x *LoginRequest) Reset() {
+	*x = LoginRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_rpc_protos_sso_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginRequest) ProtoMessage() {}
+
+func (x *LoginRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_protos_sso_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
+func (*LoginRequest) Descriptor() ([]byte, []int) {
+	return file_rpc_protos_sso_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *LoginRequest) GetCode() string {
+	if x != nil {
+		return x.Code
+	}
+	return ""
+}
+
+func (x *LoginRequest) GetSource() string {
+	if x != nil {
+		return x.Source
+	}
+	return ""
+}
+
+type LoginResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
+}
+
+func (x *LoginResponse) Reset() {
+	*x = LoginResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_rpc_protos_sso_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LoginResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginResponse) ProtoMessage() {}
+
+func (x *LoginResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_rpc_protos_sso_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead.
+func (*LoginResponse) Descriptor() ([]byte, []int) {
+	return file_rpc_protos_sso_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *LoginResponse) GetResult() string {
+	if x != nil {
+		return x.Result
+	}
+	return ""
+}
+
+var File_rpc_protos_sso_proto protoreflect.FileDescriptor
+
+var file_rpc_protos_sso_proto_rawDesc = []byte{
+	0x0a, 0x14, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x73, 0x73, 0x6f,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x72, 0x70, 0x63, 0x22, 0x3a, 0x0a, 0x0c, 0x4c,
+	0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63,
+	0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12,
+	0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x27, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75,
+	0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
+	0x32, 0x3c, 0x0a, 0x08, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x05,
+	0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x11, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x69,
+	0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c,
+	0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x10,
+	0x5a, 0x0e, 0x2e, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x73, 0x6f, 0x2f, 0x3b, 0x73, 0x73, 0x6f,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_rpc_protos_sso_proto_rawDescOnce sync.Once
+	file_rpc_protos_sso_proto_rawDescData = file_rpc_protos_sso_proto_rawDesc
+)
+
+func file_rpc_protos_sso_proto_rawDescGZIP() []byte {
+	file_rpc_protos_sso_proto_rawDescOnce.Do(func() {
+		file_rpc_protos_sso_proto_rawDescData = protoimpl.X.CompressGZIP(file_rpc_protos_sso_proto_rawDescData)
+	})
+	return file_rpc_protos_sso_proto_rawDescData
+}
+
+var file_rpc_protos_sso_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_rpc_protos_sso_proto_goTypes = []any{
+	(*LoginRequest)(nil),  // 0: rpc.LoginRequest
+	(*LoginResponse)(nil), // 1: rpc.LoginResponse
+}
+var file_rpc_protos_sso_proto_depIdxs = []int32{
+	0, // 0: rpc.SSOLogin.Login:input_type -> rpc.LoginRequest
+	1, // 1: rpc.SSOLogin.Login:output_type -> rpc.LoginResponse
+	1, // [1:2] is the sub-list for method output_type
+	0, // [0:1] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_rpc_protos_sso_proto_init() }
+func file_rpc_protos_sso_proto_init() {
+	if File_rpc_protos_sso_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_rpc_protos_sso_proto_msgTypes[0].Exporter = func(v any, i int) any {
+			switch v := v.(*LoginRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_rpc_protos_sso_proto_msgTypes[1].Exporter = func(v any, i int) any {
+			switch v := v.(*LoginResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_rpc_protos_sso_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_rpc_protos_sso_proto_goTypes,
+		DependencyIndexes: file_rpc_protos_sso_proto_depIdxs,
+		MessageInfos:      file_rpc_protos_sso_proto_msgTypes,
+	}.Build()
+	File_rpc_protos_sso_proto = out.File
+	file_rpc_protos_sso_proto_rawDesc = nil
+	file_rpc_protos_sso_proto_goTypes = nil
+	file_rpc_protos_sso_proto_depIdxs = nil
+}

+ 123 - 0
rpc/sso/sso_grpc.pb.go

@@ -0,0 +1,123 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.28.0--rc2
+// source: rpc/protos/sso.proto
+
+//protoc --go_out=. --go-grpc_out=. rpc/protos/sso.proto
+
+package sso
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+	SSOLogin_Login_FullMethodName = "/rpc.SSOLogin/Login"
+)
+
+// SSOLoginClient is the client API for SSOLogin service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type SSOLoginClient interface {
+	Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
+}
+
+type sSOLoginClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewSSOLoginClient(cc grpc.ClientConnInterface) SSOLoginClient {
+	return &sSOLoginClient{cc}
+}
+
+func (c *sSOLoginClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(LoginResponse)
+	err := c.cc.Invoke(ctx, SSOLogin_Login_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// SSOLoginServer is the server API for SSOLogin service.
+// All implementations must embed UnimplementedSSOLoginServer
+// for forward compatibility.
+type SSOLoginServer interface {
+	Login(context.Context, *LoginRequest) (*LoginResponse, error)
+	mustEmbedUnimplementedSSOLoginServer()
+}
+
+// UnimplementedSSOLoginServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedSSOLoginServer struct{}
+
+func (UnimplementedSSOLoginServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
+}
+func (UnimplementedSSOLoginServer) mustEmbedUnimplementedSSOLoginServer() {}
+func (UnimplementedSSOLoginServer) testEmbeddedByValue()                  {}
+
+// UnsafeSSOLoginServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to SSOLoginServer will
+// result in compilation errors.
+type UnsafeSSOLoginServer interface {
+	mustEmbedUnimplementedSSOLoginServer()
+}
+
+func RegisterSSOLoginServer(s grpc.ServiceRegistrar, srv SSOLoginServer) {
+	// If the following call pancis, it indicates UnimplementedSSOLoginServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
+	s.RegisterService(&SSOLogin_ServiceDesc, srv)
+}
+
+func _SSOLogin_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(LoginRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(SSOLoginServer).Login(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: SSOLogin_Login_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(SSOLoginServer).Login(ctx, req.(*LoginRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// SSOLogin_ServiceDesc is the grpc.ServiceDesc for SSOLogin service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var SSOLogin_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "rpc.SSOLogin",
+	HandlerType: (*SSOLoginServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Login",
+			Handler:    _SSOLogin_Login_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "rpc/protos/sso.proto",
+}

+ 41 - 0
rpc/sso/sso_rpc.go

@@ -0,0 +1,41 @@
+package sso
+
+import (
+	"context"
+	"eta/eta_mini_crm_ht/rpc"
+	"eta/eta_mini_crm_ht/utils"
+	"sync"
+)
+
+var (
+	once           sync.Once
+	rpcPool        utils.ClientPool
+	SSOServiceImpl *SSOService
+)
+
+type SSOService struct {
+	ssoRPCService
+}
+type ssoRPCService struct {
+	rpc.DefaultRpcClient
+}
+
+func GetSSOService() *SSOService {
+	once.Do(func() {
+		c := ssoRPCService{}
+		rpcPool = c.GetPool(utils.SSO_URL)
+		SSOServiceImpl = &SSOService{c}
+	})
+	return SSOServiceImpl
+}
+
+func (s *SSOService) SSOLogin(request *LoginRequest) {
+	conn := rpcPool.Get()
+	defer rpcPool.Put(conn)
+	ssoLoginService := NewSSOLoginClient(conn)
+	s.WrapSign(request, func(ctx context.Context, request interface{}) (err error) {
+		loginReq := request.(*LoginRequest)
+		_, err = ssoLoginService.Login(ctx, loginReq)
+		return
+	})
+}

+ 9 - 0
services/sso_service.go

@@ -0,0 +1,9 @@
+package services
+
+import "eta/eta_mini_crm_ht/rpc/sso"
+
+func SSOLogin(code string) (token string) {
+	ssoService := sso.GetSSOService()
+	ssoService.SSOLogin(&sso.LoginRequest{})
+	return
+}

+ 8 - 1
utils/config.go

@@ -2,7 +2,6 @@ package utils
 
 import (
 	"fmt"
-
 	beeLogger "github.com/beego/bee/v2/logger"
 	"github.com/beego/beego/v2/server/web"
 )
@@ -15,6 +14,7 @@ var (
 
 	REDIS_CACHE string      //缓存地址
 	Rc          RedisClient //redis缓存
+	PRCPool     ClientPool  //rpc连接池
 )
 var ObjectStorageClient string // 目前有oss minio,默认oss
 // 推送模版消息
@@ -98,6 +98,10 @@ var (
 	LogMaxDays int // 日志最大保留天数
 )
 
+// SSO登录
+var (
+	SSO_URL string
+)
 var DesKey string // 接口返回加密KEY
 
 func init() {
@@ -197,6 +201,9 @@ func init() {
 		HT_MINI_APPID = config["ht_mini_appid"]
 		RESOURCE = config["resource"]
 	}
+	{
+		SSO_URL = config["sso_url"]
+	}
 	// 初始化ES
 	initEs()
 

+ 50 - 0
utils/grpc_client_pool.go

@@ -0,0 +1,50 @@
+package utils
+
+import (
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/connectivity"
+	"sync"
+)
+
+type ClientPool interface {
+	Get() *grpc.ClientConn
+	Put(conn *grpc.ClientConn)
+}
+
+type clientPool struct {
+	pool sync.Pool
+}
+
+func GetPoolInstance(target string, opts ...grpc.DialOption) (ClientPool, error) {
+	return &clientPool{
+		pool: sync.Pool{
+			New: func() any {
+				conn, err := grpc.NewClient(target, opts...)
+				if err != nil {
+					FileLog.Error("连接sso登录服务失败:%v", err)
+				}
+				return conn
+			},
+		},
+	}, nil
+}
+
+func (c *clientPool) Get() *grpc.ClientConn {
+	conn := c.pool.Get().(*grpc.ClientConn)
+
+	// 当连接不可用时,关闭当前连接,并新建一个连接
+	if conn.GetState() == connectivity.Shutdown || conn.GetState() == connectivity.TransientFailure {
+		conn.Close()
+		conn = c.pool.New().(*grpc.ClientConn)
+	}
+	return conn
+}
+
+func (c *clientPool) Put(conn *grpc.ClientConn) {
+	// 当连接不可用时,关闭当前连接,并不再放回池中
+	if conn.GetState() == connectivity.Shutdown || conn.GetState() == connectivity.TransientFailure {
+		conn.Close()
+		return
+	}
+	c.pool.Put(conn)
+}