go-zero微服務入門教程

go-zero微服務入門教程

本教程主要模擬實現用戶注冊用戶信息查詢兩個接口。

準備工作

安裝基礎環境

  • 安裝etcd, mysql,redis,建議采用docker安裝。

MySQL安裝好之后,新建數據庫dsms_admin,并新建表sys_user,建表語句見后文。

安裝插件

這里采用GoLand開發工具,請自行搜索安裝插件Goctl。

在這里插入圖片描述

創建工程

這里采用開發工具GoLand,File > New > Project

在這里插入圖片描述

創建api目錄、rpc目錄、common目錄。
在這里插入圖片描述
在這里插入圖片描述

編寫API Gateway代碼

編寫api文件

在api目錄下創建新目錄doc/sys。

在這里插入圖片描述

在api/doc/sys下創建user.api。

在這里插入圖片描述

user.api文件內容如下:

syntax = "v1"info(title: "用戶相關"desc: "用戶相關"author: "宋發元"
)type (UserInfoResp {Code    int64        `json:"code"`Message string       `json:"message"`Data    UserInfoData `json:"data"`}UserInfoData {Avatar      string             `json:"avatar"`Name        string             `json:"name"`MenuTree    []*ListMenuTree    `json:"menuTree"`MenuTreeVue []*ListMenuTreeVue `json:"menuTreeVue"`ResetPwd    bool               `json:"resetPwd,default=false"`}ListMenuTree {Id       int64  `json:"id"`Path     string `json:"path"`Name     string `json:"name"`ParentId int64  `json:"parentId"`Icon     string `json:"icon"`}ListMenuTreeVue {Id           int64        `json:"id"`ParentId     int64        `json:"parentId"`Title        string       `json:"title"`Path         string       `json:"path"`Name         string       `json:"name"`Icon         string       `json:"icon"`VueRedirent  string       `json:"vueRedirent"`VueComponent string       `json:"vueComponent"`Meta         MenuTreeMeta `json:"meta"`}MenuTreeMeta {Title string `json:"title"`Icon  string `json:"icon"`}AddUserReq {Name     string `json:"name"`NickName string `json:"nickName"`Password string `json:"password,optional"`Email    string `json:"email"`RoleId   int64  `json:"roleId"`Status   int64  `json:"status,default=1"`}AddUserResp {Code    int64           `json:"code"`Message string          `json:"message"`Data    ReceiptUserData `json:"data"`}ReceiptUserData {Id int64 `json:"id"`}
)@server (group : sys/userprefix : /sys/user
)service admin-api{@doc(summary : "用戶管理-獲取當前用戶信息")@handler UserInfoget /currentUser returns (UserInfoResp)@doc(summary : "用戶管理-新增用戶")@handler UserAddpost /add(AddUserReq)returns(AddUserResp)
}

用goctl生成API Gateway代碼

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

生成的文件結構如下:

api
├── admin.go    //main入口定義
├── doc
│   └── sys
│       └── user.api    //api定義文件
├── etc
│   └── admin-api.yaml    //配置文件
└── internal├── config│   └── config.go    //定義配置├── handler│   ├── routes.go   //定義路由處理│   └── sys│       └── user│           ├── useraddhandler.go    //實現addhandler│           └── userinfohandler.go    //實現infohandler├── logic│   └── sys│       └── user│           ├── useraddlogic.go    //實現addlogic│           └── userinfologic.go    //實現infologic├── svc│   └── servicecontext.go    //定義ServiceContext└── types└── types.go    //定義請求、返回結構體

編寫rpc服務

編寫sys.proto文件

在rpc下創建新目錄sys。
在這里插入圖片描述

在rpc/sys目錄下創建sys.proto文件。

在這里插入圖片描述

sys.proto文件內容如下:

syntax = "proto3";package sysclient;option go_package = "./sysclient";message InfoReq{int64 UserId = 1;
}
message InfoResp{string avatar =1;string name = 2;repeated MenuListTree menuListTree = 3;repeated string backgroundUrls=4;bool resetPwd=5;
}message MenuListTree{int64 id=1;string name=2;string icon=3;int64 parentId=4;string path=5;string vuePath=6;string vueComponent=7;string vueIcon=8;string vueRedirect=9;string backgroundUrl=10;
}message UserAddReq{string name=1;string nickName=2;string password=3;string email=4;int64 roleId=5;int64 status=6;string createBy=7;
}message  UserAddResp{int64 id=1;
}service Sys{rpc UserInfo(InfoReq)returns(InfoResp);rpc UserAdd(UserAddReq)returns(UserAddResp);
}

用goctl生成rpc代碼

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

生成的文件結構如下:

sys
├── etc
│   └── sys.yaml                     //yaml配置文件
├── internal
│   ├── config
│   │   └── config.go                //yaml配置文件對應的結構體定義
│   ├── logic                        //業務邏輯
│   │   ├── useraddlogic.go
│   │   └── userinfologic.go
│   ├── server                       //rpc server
│   │   └── sysserver.go
│   └── svc                          //資源依賴
│       └── servicecontext.go
├── sys                              //rpc client call entry
│   └── sys.go
├── sys.go                           //main函數入口
├── sys.proto                        //proto源文件
└── sysclient                        //pb.go├── sys.pb.go└── sys_grpc.pb.go

修改API Gateway代碼調用rpc服務

admin-api.yaml

  • 修改配置文件admin-api.yaml,增加如下內容,這里的192.168.2.204為基礎環境服務器IP。
Timeout: 60000Mysql:Datasource: root:123456@tcp(192.168.2.204:3306)/dsms_admin?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghaiCacheRedis:- Host: 192.168.2.204:6379Pass: qkgxChxNkCwKType: nodeRedis:Address: 192.168.2.204:6379Pass: qkgxChxNkCwKSysRpc:Timeout: 30000Etcd:Hosts:- 192.168.2.204:2379Key: sysa.rpc

通過etcd自動去發現可用的rpc服務。

config.go

  • 修改api/internal/config/config.go如下,增加rpc服務依賴。
	SysRpc zrpc.RpcClientConfCacheRedis cache.ClusterConfRedis struct {Address stringPass    string}Mysql struct {Datasource string}

servicecontext.go

  • 修改api/internal/svc/servicecontext.go,如下:
type ServiceContext struct {Config config.ConfigSys   sys.SysRedis *redis.Redis
}func NewServiceContext(c config.Config) *ServiceContext {newRedis := redis.New(c.Redis.Address, redisConfig(c))return &ServiceContext{Config: c,Sys:    sys.NewSys(zrpc.MustNewClient(c.SysRpc, zrpc.WithUnaryClientInterceptor(interceptor))),Redis:  newRedis,}
}func redisConfig(c config.Config) redis.Option {return func(r *redis.Redis) {r.Type = redis.NodeTyper.Pass = c.Redis.Pass}
}func interceptor(ctx context.Context, method string, req any, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {md := metadata.New(map[string]string{"x": "xx"})ctx = metadata.NewOutgoingContext(ctx, md)// logx.Debug("調用rpc服務前")err := invoker(ctx, method, req, reply, cc)if err != nil {return err}// logx.Debug("調用rpc服務后")return nil
}

通過ServiceContext在不同業務邏輯之間傳遞依賴。

useraddlogic.go

  • 修改api/internal/logic/useraddlogic.go里的UserAdd方法,如下:
func (l *UserAddLogic) UserAdd(req *types.AddUserReq) (resp *types.AddUserResp, err error) {res, err := l.svcCtx.Sys.UserAdd(l.ctx, &sysclient.UserAddReq{Name:     req.Name,NickName: req.NickName,Password: req.Password,Email:    req.Email,RoleId:   req.RoleId,Status:   req.Status,//CreateBy: l.ctx.Value(cache.JwtFieldUserName).(string),CreateBy: "songfayuan",})if err != nil {reqJson, _ := json.Marshal(req)logx.WithContext(l.ctx).Errorf("添加用戶信息失敗,請求參數:%s,異常信息:%s", reqJson, err.Error())return nil, rpcerror.New(err)}return &types.AddUserResp{Code:    200,Message: "添加用戶成功",Data:    types.ReceiptUserData{Id: res.Id},}, nil
}

userinfologic.go

  • 修改api/internal/logic/userinfologic.go里的UserInfo方法,如下:

func (l *UserInfoLogic) UserInfo() (*types.UserInfoResp, error) {//這里的key和生成jwt token時傳入的key一致//userId, _ := l.ctx.Value(cache.JwtFieldUserId).(json.Number).Int64()var userId int64 = 1resp, err := l.svcCtx.Sys.UserInfo(l.ctx, &sysclient.InfoReq{UserId: userId,})if err != nil {logx.WithContext(l.ctx).Errorf("根據userId:%s, 查詢用戶異常:%s", strconv.FormatInt(userId, 10), err.Error())return nil, rpcerror.New(err)}var MenuTree []*types.ListMenuTree//組裝ant ui中的菜單for _, item := range resp.MenuListTree {MenuTree = append(MenuTree, &types.ListMenuTree{Id:       item.Id,Path:     item.Path,Name:     item.Name,ParentId: item.ParentId,Icon:     item.Icon,})}if MenuTree == nil {MenuTree = make([]*types.ListMenuTree, 0)}//組裝element ui中的菜單var MenuTreeVue []*types.ListMenuTreeVuefor _, item := range resp.MenuListTree {if len(strings.TrimSpace(item.VuePath)) != 0 {MenuTreeVue = append(MenuTreeVue, &types.ListMenuTreeVue{Id:           item.Id,ParentId:     item.ParentId,Title:        item.Name,Path:         item.VuePath,Name:         item.Name,Icon:         item.VueIcon,VueRedirent:  item.VueRedirect,VueComponent: item.VueComponent,Meta: types.MenuTreeMeta{Title: item.Name,Icon:  item.VueIcon,},})}}if MenuTreeVue == nil {MenuTreeVue = make([]*types.ListMenuTreeVue, 0)}err = l.svcCtx.Redis.Set(strconv.FormatInt(userId, 10), strings.Join(resp.BackgroundUrls, ","))if err != nil {logx.Errorf("設置用戶:%s, 權限到Redis異常:%+v", resp.Name, err)}return &types.UserInfoResp{Code:    200,Message: "成功",Data: types.UserInfoData{Avatar:      resp.Avatar,Name:        resp.Name,MenuTree:    MenuTree,MenuTreeVue: MenuTreeVue,ResetPwd:    resp.ResetPwd,},}, nil
}

定義數據庫表結構,并生成CRUD代碼

  • 在rpc目錄下創建model/sysmodel目錄,在rpc目錄下創建doc/sql/sys目錄。

在這里插入圖片描述

創建sys_user.sql

  • 在rpc/doc/sql/sys目錄下編寫sql文件sys_user.sql,如下:
-- goctl model mysql ddl -src doc/sql/sys/sys_user.sql -dir ./model/sysmodel-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`
(`id`          bigint(20) NOT NULL AUTO_INCREMENT COMMENT '編號',`name`        varchar(128) NOT NULL DEFAULT '' COMMENT '賬號',`nick_name`   varchar(128) NOT NULL DEFAULT '' COMMENT '名稱',`avatar`      varchar(255) NOT NULL DEFAULT '' COMMENT '頭像',`password`    varchar(128) NOT NULL DEFAULT '' COMMENT '密碼',`salt`        varchar(40)  NOT NULL DEFAULT '' COMMENT '加密鹽',`email`       varchar(128) NOT NULL DEFAULT '' COMMENT '郵箱',`mobile`      varchar(32)  NOT NULL DEFAULT '' COMMENT '手機號',`status`      tinyint(4) NOT NULL DEFAULT '1' COMMENT '狀態  -1:禁用   1:正常',`create_by`   varchar(128) NOT NULL DEFAULT '' COMMENT '創建人',`create_time` timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`update_by`   varchar(128) NOT NULL DEFAULT '' COMMENT '更新人',`update_time` datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',`del_flag`    tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '是否刪除  1:已刪除  0:正常',PRIMARY KEY (`id`),KEY           `name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用戶管理';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, 'admin', 'admin', '', '$2a$10$hDlSis2/3IPGNYQhFlFfK.Wmi7iH9/jr6wcN.5c.rh7fc/uUnCo4S', '', 'admin@dsms.com', '13612345678', 1, 'admin', '2018-08-14 11:11:11', '', '2023-01-04 10:17:30', 0);

生成CRUD代碼

方法一

通過工具生成,這種方式生成帶緩存的代碼。(本文采用方法二生成)

在這里插入圖片描述

選擇代碼位置。
在這里插入圖片描述

生成的代碼。

在這里插入圖片描述

方法二(采納)

在rpc路徑下執行如下命令,生成不帶緩存的代碼。

goctl model mysql ddl -src doc/sql/sys/sys_user.sql -dir ./model/sysmodel

完善CRUD代碼

sysusermodel.go

在model/sysmodel/sysusermodel.go文件中添加常用crud的代碼,完整代碼如下。

package sysmodelimport ("context""errors""github.com/zeromicro/go-zero/core/stores/sqlx""time"
)
import sq "github.com/Masterminds/squirrel"var _ SysUserModel = (*customSysUserModel)(nil)type (// SysUserModel is an interface to be customized, add more methods here,// and implement the added methods in customSysUserModel.SysUserModel interface {sysUserModelwithSession(session sqlx.Session) SysUserModelTrans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) errorUpdateBuilder() sq.UpdateBuilderUpdateByQuery(ctx context.Context, updateBuilder sq.UpdateBuilder) errorRowBuilder() sq.SelectBuilderFindOneByQuery(ctx context.Context, rowBuilder sq.SelectBuilder) (*SysUser, error)FindRowsByQuery(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUser, error)CountBuilder(field string) sq.SelectBuilderFindCount(ctx context.Context, countBuilder sq.SelectBuilder) (int64, error)FindAll(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUserList, error)TableName() string}customSysUserModel struct {*defaultSysUserModel}SysUserList struct {Id         int64     `db:"id"`          // 編號Name       string    `db:"name"`        // 賬號NickName   string    `db:"nick_name"`   // 名稱Avatar     string    `db:"avatar"`      // 頭像Password   string    `db:"password"`    // 密碼Salt       string    `db:"salt"`        // 加密鹽Email      string    `db:"email"`       // 郵箱Mobile     string    `db:"mobile"`      // 手機號Status     int64     `db:"status"`      // 狀態  -1:禁用   1:正常CreateBy   string    `db:"create_by"`   // 創建人CreateTime time.Time `db:"create_time"` // 創建時間UpdateBy   string    `db:"update_by"`   // 更新人UpdateTime time.Time `db:"update_time"` // 更新時間DelFlag    int64     `db:"del_flag"`    // 是否刪除  1:已刪除  0:正常RoleId     int64     `db:"role_id"`RoleName   string    `db:"role_name"`}
)func (m *customSysUserModel) UpdateByQuery(ctx context.Context, updateBuilder sq.UpdateBuilder) error {query, values, err := updateBuilder.Where("del_flag = ?", 0).ToSql()if err != nil {return err}_, err = m.conn.ExecCtx(ctx, query, values...)return err
}func (m *customSysUserModel) UpdateBuilder() sq.UpdateBuilder {return sq.Update(m.table)
}func (m *customSysUserModel) Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error {return m.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {return fn(ctx, session)})
}func (m *customSysUserModel) TableName() string {return m.table
}func (m *customSysUserModel) FindAll(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUserList, error) {if orderBy == "" {rowBuilder = rowBuilder.OrderBy("id AEC")} else {rowBuilder = rowBuilder.OrderBy(orderBy)}query, values, err := rowBuilder.Where("del_flag = ?", 0).ToSql()if err != nil {return nil, err}var resp []*SysUserListerr = m.conn.QueryRowsCtx(ctx, &resp, query, values...)switch err {case nil:return resp, nilcase sqlx.ErrNotFound:return nil, errors.New("查詢記錄為空")default:return nil, err}
}func (m *customSysUserModel) FindCount(ctx context.Context, countBuilder sq.SelectBuilder) (int64, error) {query, values, err := countBuilder.Where("del_flag = ?", 0).ToSql()if err != nil {return 0, err}var resp int64err = m.conn.QueryRowCtx(ctx, &resp, query, values...)switch err {case nil:return resp, nildefault:return 0, err}
}func (m *customSysUserModel) CountBuilder(field string) sq.SelectBuilder {return sq.Select("COUNT(" + field + ")").From(m.table)
}func (m *customSysUserModel) FindRowsByQuery(ctx context.Context, rowBuilder sq.SelectBuilder, orderBy string) ([]*SysUser, error) {if orderBy == "" {rowBuilder = rowBuilder.OrderBy("id DESC")} else {rowBuilder = rowBuilder.OrderBy(orderBy)}query, values, err := rowBuilder.Where("del_flag = ?", 0).ToSql()if err != nil {return nil, err}var resp []*SysUsererr = m.conn.QueryRowCtx(ctx, &resp, query, values...)switch err {case nil:return resp, nilcase sqlx.ErrNotFound:return nil, errors.New("查詢記錄為空")default:return nil, err}
}func (m *customSysUserModel) FindOneByQuery(ctx context.Context, rowBuilder sq.SelectBuilder) (*SysUser, error) {query, values, err := rowBuilder.Where("del_flag = ?", 0).Limit(1).ToSql()if err != nil {return nil, err}var resp SysUsererr = m.conn.QueryRowCtx(ctx, &resp, query, values...)switch err {case nil:return &resp, nildefault:return nil, err}
}func (m *customSysUserModel) RowBuilder() sq.SelectBuilder {return sq.Select(sysUserRows).From(m.table)
}// NewSysUserModel returns a model for the database table.
func NewSysUserModel(conn sqlx.SqlConn) SysUserModel {return &customSysUserModel{defaultSysUserModel: newSysUserModel(conn),}
}func (m *customSysUserModel) withSession(session sqlx.Session) SysUserModel {return NewSysUserModel(sqlx.NewSqlConnFromSession(session))
}

修改rpc代碼調用crud代碼

sys.yaml

  • 修改rpc/sys/etc/sys.yaml,如下內容:
Name: sys.rpc
ListenOn: 0.0.0.0:8080
Timeout: 10000
Etcd:Hosts:- 192.168.2.204:2379Key: sysa.rpcMysql:Datasource: root:123456@tcp(192.168.2.204:3306)/dsms_admin?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghaiCacheRedis:- Host: 192.168.2.204:6379Pass: qkgxChxNkCwKType: node

config.go

  • 修改rpc/sys/internal/config/config.go,如下:
type Config struct {zrpc.RpcServerConfMysql struct {Datasource string}CacheRedis cache.ClusterConf
}

servicecontext.go

  • 修改rpc/sys/internal/svc/servicecontext.go,如下:
type ServiceContext struct {Config config.ConfigCache  cache.CacheRedis  *redis.RedisUserModel sysmodel.SysUserModel
}func NewServiceContext(c config.Config) *ServiceContext {sqlConn := sqlx.NewMysql(c.Mysql.Datasource)ca := cache.New(c.CacheRedis, syncx.NewSingleFlight(), cache.NewStat("dc"), errors.New("data not find"))rConn := redis.New(c.CacheRedis[0].Host, func(r *redis.Redis) {r.Type = c.CacheRedis[0].Typer.Pass = c.CacheRedis[0].Pass})return &ServiceContext{Config: c,Cache:  ca,Redis:  rConn,UserModel: sysmodel.NewSysUserModel(sqlConn),}
}

useraddlogic.go

  • 修改rpc/sys/internal/logic/useraddlogic.go,如下:
func (l *UserAddLogic) UserAdd(in *sysclient.UserAddReq) (*sysclient.UserAddResp, error) {if in.Name == "" {return nil, errors.New("賬號不能為空")}if in.NickName == "" {return nil, errors.New("姓名不能為空")}if in.Email == "" {return nil, errors.New("郵箱不能為空")}//校驗賬號是否已存在selectBuilder := l.svcCtx.UserModel.CountBuilder("id").Where(sq.Eq{"name": in.Name})count, _ := l.svcCtx.UserModel.FindCount(l.ctx, selectBuilder)if count > 0 {logx.WithContext(l.ctx).Errorf("賬號已存在,添加失敗,userName = %s", in.Name)return nil, errors.New("賬號已存在")}if in.Password == "" {in.Password = "123456"}hashedPassword, err := utils.GenerateFromPassword(in.Password)if err != nil {return nil, errors.New("密碼加密出錯")}//插入數據result, err := l.svcCtx.UserModel.Insert(l.ctx, &sysmodel.SysUser{Name:       in.Name,NickName:   in.NickName,Avatar:     "",Password:   hashedPassword,Salt:       "",Email:      in.Email,Mobile:     "",Status:     0,CreateBy:   in.CreateBy,UpdateTime: time.Time{},DelFlag:    0,})if err != nil {return nil, err}insertId, err := result.LastInsertId()if err != nil {return nil, err}return &sysclient.UserAddResp{Id: insertId}, nil
}

userinfologic.go

  • 修改rpc/sys/internal/logic/userinfologic.go,如下:
func (l *UserInfoLogic) UserInfo(in *sysclient.InfoReq) (*sysclient.InfoResp, error) {rowBuilder := l.svcCtx.UserModel.RowBuilder().Where(sq.Eq{"id": in.UserId})userInfo, err := l.svcCtx.UserModel.FindOneByQuery(l.ctx, rowBuilder)switch err {case nil:case sqlx.ErrNotFound:logx.WithContext(l.ctx).Infof("用戶不存在userId:%s", in.UserId)return nil, fmt.Errorf("用戶不存在userId:%s", strconv.FormatInt(in.UserId, 10))default:return nil, err}//var list []*sys.MenuListTree//var listUrls []stringreturn &sysclient.InfoResp{Avatar:         "11111",Name:           userInfo.Name,MenuListTree:   nil,BackgroundUrls: nil,ResetPwd:       false,}, nil
}

common目錄

common目錄下為通用工具,直接拷貝進去即可。

bcrypt.go

  • common/utils/bcrypt.go
package utilsimport ("bytes""crypto/md5""crypto/rand""crypto/rsa""crypto/x509""encoding/base64""encoding/hex""encoding/pem""fmt""log""github.com/tjfoc/gmsm/sm2"x509g "github.com/tjfoc/gmsm/x509""golang.org/x/crypto/bcrypt"
)func GenerateFromPassword(pwd string) (hashedPassword string, err error) {password := []byte(pwd)// Hashing the password with the default cost of 10hashedPasswordBytes, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)hashedPassword = string(hashedPasswordBytes)return
}func CompareHashAndPassword(hashedPwd, plainPwd string) bool {byteHash := []byte(hashedPwd)err := bcrypt.CompareHashAndPassword(byteHash, []byte(plainPwd))if err != nil {return false}return true
}// EncryptSm2 加密
func EncryptSm2(privateKey, content string) string {// 從十六進制導入公私鑰priv, err := x509g.ReadPrivateKeyFromHex(privateKey)if err != nil {log.Fatal(err)}// 公鑰加密部分msg := []byte(content)pub := &priv.PublicKeycipherTxt, err := sm2.Encrypt(pub, msg, rand.Reader, sm2.C1C2C3) // sm2加密if err != nil {log.Fatal(err)}// fmt.Printf("加密文字:%s\n加密結果:%x\n", msg, cipherTxt)encodeRes := fmt.Sprintf("%x", cipherTxt)return encodeRes
}// DecryptSm2 解密
func DecryptSm2(privateKey, encryptData string) (string, error) {// 從十六進制導入公私鑰priv, err := x509g.ReadPrivateKeyFromHex(privateKey)if err != nil {return "", err}// 私鑰解密部分hexData, err := hex.DecodeString(encryptData)if err != nil {return "", err}plainTxt, err := sm2.Decrypt(priv, hexData, sm2.C1C2C3) // sm2解密if err != nil {return "", err}// fmt.Printf("解密后的明文:%s\n私鑰:%s \n 匹配一致", plainTxt, x509.WritePrivateKeyToHex(priv))return string(plainTxt), nil
}// EncryptAndDecrypt 加密/解密
func EncryptAndDecrypt(privateKey, content string) {// 從十六進制導入公私鑰priv, err := x509g.ReadPrivateKeyFromHex(privateKey)if err != nil {log.Fatal(err)}// 公鑰加密部分msg := []byte(content)pub := &priv.PublicKeycipherTxt, err := sm2.Encrypt(pub, msg, rand.Reader, sm2.C1C2C3) // sm2加密if err != nil {log.Fatal(err)}fmt.Printf("加密文字:%s\n加密結果:%x\n", msg, cipherTxt)// 私鑰解密部分plainTxt, err := sm2.Decrypt(priv, cipherTxt, sm2.C1C2C3) // sm2解密if err != nil {log.Fatal(err)}if !bytes.Equal(msg, plainTxt) {log.Fatal("原文不匹配:", msg)}fmt.Printf("解密后的明文:%s\n私鑰:%s \n 匹配一致", plainTxt, x509g.WritePrivateKeyToHex(priv))
}// EncryptRSA 加密
func EncryptRSA(content, publicKey string) (encryptStr string, err error) {// 	var publicKey = `-----BEGIN PUBLIC KEY-----// MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaIWAL13RU+bJN2hfmTSyOBotf// 71pq8jc2ploPBHtN3smTUkYPbX2MIbO9TrRj3u67s/kGQZrz6tyQ68oexpukPN4/// ypzp64UA5CQENSA41ZxTpYADbFQsiX9Spv6aDHhHzUlZtWRru9ptcFO3tDKq0ACT// OAR1ZEHFwQGhzwaAowIDAQAB// -----END PUBLIC KEY-----`block, _ := pem.Decode([]byte(publicKey))if block == nil {return "", fmt.Errorf("failed to parse public key PEM")}publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)if err != nil {return "", err}//  類型斷言rsaPublicKey := publicKeyInterface.(*rsa.PublicKey)// 加密數據encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(content))if err != nil {return "", fmt.Errorf("error encrypting data:%v", err)}return base64.StdEncoding.EncodeToString(encryptedData), err}// DecryptRSA 解密
func DecryptRSA(encryptStr, privateKey string) (content string, err error) {// 	var privateKey = `-----BEGIN PRIVATE KEY-----// MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANohYAvXdFT5sk3a// F+ZNLI4Gi1/vWmryNzamWg8Ee03eyZNSRg9tfYwhs71OtGPe7ruz+QZBmvPq3JDr// yh7Gm6Q83j/KnOnrhQDkJAQ1IDjVnFOlgANsVCyJf1Km/poMeEfNSVm1ZGu72m1w// U7e0MqrQAJM4BHVkQcXBAaHPBoCjAgMBAAECgYA/aJJN/uyvQwKlBPALn4WDJ73e// PmrvScfpGAR39xqM8WVxcOoy0+Y6FRX1wupHWefWIqQSQIH1w+EoM5LGzX8yflSo// lG3E0mgJzrMAOTs5FVkdN4tV6rKYq/vA9R67AD0a9nq7yOFeTqjGzWj4l7Vptvu4// prK5GWV+i0+mpB2kKQJBAP0n1EMAHQSW38zOngfaqC6cvnjEbX4NnhSPRZVzlu3y// ZkitiA/Y96yCCybCWD0TkF43Z1p0wIGuXSJ1Igku6bcCQQDclMziUz1RnQDl7RIN// 449vbmG2mGLoXp5HTD9QP0NB46w64WwXIX7IZL2GubndTRFUFTTPLZZ80XbhFtp6// 19B1AkEAnIgjJGaOisbrjQz5BCw8r821rKDwfu/WninUwcteOLUYb7n1Fq92vZEP// aiDjRKizLL6fRnxIiCcTaXn52KnMUwJBAJaKOxYPRx8G7tD8rcCq2H5tL+TFNWNv// B8iTAfbLZiR2tFlu9S0IIBW1ox9qa63b5gKjgmoOq9C9x8swpKUH2u0CQAKDHqwh// aH6lVtV8cw55Ob8Dsh3PgFUazuM1+e5PjmZku3/2jeQQJrecu/S6LooPdeUf+EtV// OB/5HvFhGpEu2/E=// -----END PRIVATE KEY-----`block, _ := pem.Decode([]byte(privateKey))if block == nil {return "", fmt.Errorf("failed to parse private key PEM")}privateKeyData, err := x509.ParsePKCS8PrivateKey(block.Bytes)if err != nil {return "", err}privateKeyInterface := privateKeyData.(*rsa.PrivateKey)// 解密數據byt, err := base64.StdEncoding.DecodeString(encryptStr)if err != nil {return "", fmt.Errorf("base64 DecodeString err:%v", err)}decryptedData, err := rsa.DecryptPKCS1v15(rand.Reader, privateKeyInterface, byt)if err != nil {return "", fmt.Errorf("error decrypting data:%v", err)}return string(decryptedData), nil}func Md5(s []byte) string {m := md5.New()m.Write(s)return hex.EncodeToString(m.Sum(nil))
}

code.go

  • common/errors/code.go
package errorsconst BaseCode = 50000const RpcCode = 51000const MustUpdatePwdCode = 50005const LoginExpired = 50001

base.go

  • common/errors/base.go
package errorstype CommonError interface {Error() stringErrorType() stringData() *CommonErrorResp
}type CommonErrorResp struct {Code    int    `json:"code"`Message string `json:"message"`Type    string `json:"error"`
}

errorx.go

  • common/errors/errorx/errorx.go
package errorximport "go-zero-test/common/errors"var _ errors.CommonError = (*ErrorX)(nil)type ErrorX struct {Code    int    `json:"code"`Message string `json:"message"`Type    string `json:"error"`
}func (e *ErrorX) Error() string {return e.Message
}func (e *ErrorX) ErrorType() string {return e.Type
}func (e *ErrorX) Data() *errors.CommonErrorResp {return &errors.CommonErrorResp{Code:    e.Code,Message: e.Message,Type:    e.Type,}
}func New(s string) error {return &ErrorX{Code: errors.BaseCode, Message: s, Type: "base error"}
}func NewCodeErr(code int, s string) error {return &ErrorX{Code: code, Message: s, Type: "base error"}
}

rpcerror.go

  • common/errors/rpcerror/rpcerror.go
package rpcerrorimport "go-zero-test/common/errors"var _ errors.CommonError = (*RpcError)(nil)type RpcError struct {Code    int    `json:"code"`Message string `json:"message"`Type    string `json:"error"`
}func (e *RpcError) Error() string {return e.Message
}func (e *RpcError) ErrorType() string {return e.Type
}func (e *RpcError) Data() *errors.CommonErrorResp {return &errors.CommonErrorResp{Code:    e.Code,Message: e.Message,Type:    e.Type,}
}// New rpc返回錯誤
func New(e error) error {msg := e.Error()[len("rpc error: code = Unknown desc = "):]return &RpcError{Code: errors.RpcCode, Message: msg, Type: "rpc error"}
}// NewError 返回自定義錯誤,rpc返回錯誤
func NewError(s string, err error) error {msgType := err.Error()[len("rpc error: code = Unknown desc = "):]return &RpcError{Code: errors.RpcCode, Message: s, Type: msgType}
}

完整調用演示

最后,在根目錄go-zero-test執行下命令。

go mod tidy

運行rpc服務

在這里插入圖片描述

修改路徑。

在這里插入圖片描述

之后直接啟動即可。

在這里插入圖片描述

運行api

在這里插入圖片描述

修改路徑。

在這里插入圖片描述

之后直接啟動即可。

在這里插入圖片描述

api調用

命令請求:

curl -i "localhost:8888/sys/user/currentUser"

返回結果:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Traceparent: 00-7cf8f53fe7009655963024f44767cd53-67d21fe606d82a15-00
Date: Thu, 22 Feb 2024 06:27:28 GMT
Content-Length: 120{"code":200,"message":"成功","data":{"avatar":"11111","name":"admin","menuTree":[],"menuTreeVue":[],"resetPwd":false}}%

或者postman調用也行。

在這里插入圖片描述

后續研發

后續新增服務、新增接口流程同編寫rpc服務模塊。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696932.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696932.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696932.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Git】 刪除遠程分支

Git 刪除遠程分支有以下幾種方法 服務端UI工具 Git 的服務端圖形化工具主要是 web 端。常用的有 GitHub、Gitea、Gutlab 等。 這些工具都提供了分支管理,可以直接在各服務端找到相關功能,謹慎刪除。 客戶端UI工具 Git 擁有諸多客戶端 UI 工具&#x…

詳細分析Python中的unittest測試框架

目錄 1. 基本知識2. API2.1 斷言2.2 setUp() 和 tearDown() 3. Demo 1. 基本知識 unittest 是 Python 標準庫中的一個單元測試框架,用于編寫和執行測試用例以驗證代碼的正確性 提供了一種結構化的方法來編寫測試,使得測試代碼更加模塊化和易于維護 以…

【ACW 服務端】頁面操作Java增刪改查代碼生成

版本: 1.2.2-JDK17-SNAPSHOT 項目地址:wu-smart-acw 演示地址:演示地址 admin/admin Java增刪改查代碼生成 找到對應菜單 選擇你需要的數據實例 選擇數據庫 選擇數據庫表 選擇客戶端(如果是本地ACW服務代碼啟動默認注冊上的客戶端ID是…

騰訊云主機Ubuntu22.04安裝Odoo17

一、安裝PostgreSQL16 參見之前的文章 Ubuntu22.04安裝PostgreSQL-CSDN博客 二、安裝Odoo17 本方案使用的nightly版的odoo,安裝的都是最新版odoo wget -O - https://nightly.odoo.com/odoo.key | apt-key add - echo "deb http://nightly.odoo.com/17.0/n…

Maven【1】(命令行操作)

文章目錄 一丶創建maven工程二、理解pom.xml三、maven的構建命令1.編譯操作2.清理操作3.測試操作4.打包操作5.安裝操作 一丶創建maven工程 首先創建這樣一個目錄,然后從命令行里進入這個目錄: 然后接下來就在這個命令行里進行操作了。 這個命令是&…

Python學習筆記——PySide6設計GUI應用之UI與邏輯分離

1、打開PySide6的UI設計工具pyside6-designer,設計一個主窗口,保存文件名為testwindow.ui 2、使用PySide6的RCC工具把testwindow.ui文件轉換為testwindow_rc.py文件,此文件中有一個類Ui_MainWindow(包含各種控件對象)…

設計模式淺析(八) ·外觀模式

設計模式淺析(八) 外觀模式 日常叨逼叨 java設計模式淺析,如果覺得對你有幫助,記得一鍵三連,謝謝各位觀眾老爺😁😁 外觀模式 概念 外觀模式(Facade Pattern)是一種設計模式,它為…

深度學習發展里程碑事件2006-2024

2006-2024年,深度學習發展經歷眾多的里程碑事件,一次次地刺激著人們的神經,帶來巨大的興奮。電影還在繼續,好戲在后面,期待…… 2006年 深度信念網絡(DBNs):Geoffrey Hinton與他的學…

備戰藍橋杯 Day10(背包dp)

01背包問題 1267:【例9.11】01背包問題 【題目描述】 一個旅行者有一個最多能裝 M� 公斤的背包,現在有 n� 件物品,它們的重量分別是W1,W2,...,Wn�1,�2&#…

藍橋杯刷題--python-10(2023填空題3)

0工作時長 - 藍橋云課 (lanqiao.cn) import datetime time_str_list=[] while(True):tmp=input()if not tmp: breaktime_str_list.append(tmp)# time_list=[datetime.datetime.strptime(t,"%Y-%m-%d %H:%M:%S")for t in time_str_list] time_list.sort() sum=0 for i…

【代碼隨想錄算法訓練營Day25】● 216.組合總和III ● 17.電話號碼的字母組合

文章目錄 Day 25 第七章 回溯算法part02216.組合總和III自己的思路(?通過) 17.電話號碼的字母組合思路代碼 Day 25 第七章 回溯算法part02 今日內容: ● 216.組合總和III● 17.電話號碼的字母組合 216.組合總和III 如果把 組合問題理解了…

計算機組成原理(9)----硬布線控制器

控制單元CU若想發出對應的控制信號,則需要以下信息:指令操作碼,目前的機器周期,節拍信號,機器狀態條件,根據這些信息,CU就能確定在這個節拍下應該發出哪些"微命令",也就是…

SQL注入:使用預編譯防御SQL注入時產生的問題

目錄 前言 模擬預編譯 真正的預編譯 預編譯中存在的SQL注入 寬字節 沒有進行參數綁定 無法預編譯的位置 前言 相信學習過SQL注入的小伙伴都知道防御SQL注入最好的方法,就是使用預編譯也就是PDO是可以非常好的防御SQL注入的,但是如果錯誤的設置了…

計算機設計大賽 深度學習動物識別 - 卷積神經網絡 機器視覺 圖像識別

文章目錄 0 前言1 背景2 算法原理2.1 動物識別方法概況2.2 常用的網絡模型2.2.1 B-CNN2.2.2 SSD 3 SSD動物目標檢測流程4 實現效果5 部分相關代碼5.1 數據預處理5.2 構建卷積神經網絡5.3 tensorflow計算圖可視化5.4 網絡模型訓練5.5 對貓狗圖像進行2分類 6 最后 0 前言 &#…

從零學算法238

238.給你一個整數數組 nums,返回 數組 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘積 。 題目數據 保證 數組 nums之中任意元素的全部前綴元素和后綴的乘積都在 32 位 整數范圍內。 請 不要使用除法,且在 O(n) 時間復…

Python自動化UI測試之Selenium基礎實操

1. Selenium簡介 Selenium 是一個用于 Web 應用程序測試的工具。最初是為網站自動化測試而開發的,可以直接運行在瀏覽器上,支持的瀏覽器包括 IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Googl…

SVN忽略已提交的文件(ignore,移出版本控制)

本文適用于已安裝TortoiseSVN客戶端的同學。 1、右鍵點擊要忽略的文件夾或文件,鼠標移到“TortoiseSVN”,找到“Unversion and add to ignore list”,選擇文件夾,彈出提示框確認忽略。 2、設置完忽略文件后,還需要做…

多維時序 | Matlab實現GRU-MATT門控循環單元融合多頭注意力多變量時間序列預測模型

多維時序 | Matlab實現GRU-MATT門控循環單元融合多頭注意力多變量時間序列預測模型 目錄 多維時序 | Matlab實現GRU-MATT門控循環單元融合多頭注意力多變量時間序列預測模型預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.多維時序 | Matlab實現GRU-MATT門控循環單元融…

【Maven】介紹、下載及安裝、集成IDEA

目錄 一、什么是Maven Maven的作用 Maven模型 Maven倉庫 二、下載及安裝 三、IDEA集成Maven 1、POM配置詳解 2、配置Maven環境 局部配置 全局設置 四、創建Maven項目 五、Maven坐標詳解 六、導入Maven項目 方式1:使用Maven面板,快速導入項目 …

React Native框架開發介紹,以及其優點

大家好,我是咕嚕鐵蛋,在今天的文章中,我通過科技手段和大家一起探討一下React Native框架的開發介紹以及其優點。我深知選擇合適的開發工具對于項目的成功至關重要。而React Native作為一款流行的跨平臺移動應用開發框架,其獨特之…