简介

小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。

系列

  1. 云原生 API 网关,gRPC-Gateway V2 初探

业务流程

初始化项目

开发环境

为少 的本地开发环境

go version
# go version go1.14.14 darwin/amd64
protoc --version
# libprotoc 3.15.7
protoc-gen-go --version
# protoc-gen-go v1.26.0
protoc-gen-go-grpc --version
# protoc-gen-go-grpc 1.1.0
protoc-gen-grpc-gateway --version

初始代码结构

使用 go mod init server 初始化 Go 项目,这里(demo)我直接采用 server 作为当前 module 名字。

go-grpc-gateway-v2-microservice

├── auth // 鉴权微服务
│   ├── api
│   ├── ├── gen
│   ├── ├── ├── v1 // 生成的代码将放到这里,v1 表示第一个 API 版本
│   │   ├── auth.proto
│   │   └── auth.yaml
│   ├── auth
│   │   └── auth.go // service 的具体实现
│   ├── wechat
│   └── main.go // 鉴权 gRPC server
├── gateway // gRPC-Gateway,反向代理到各个 gRPC Server
│   └── main.go
├── gen.sh // 根据 `auth.proto` 生成代码的命令
└── go.mod

领域(auth.proto)定义

syntax = "proto3";
package auth.v1;
option go_package="server/auth/api/gen/v1;authpb"; // 客户端发送一个 code
message LoginRequest {
string code = 1;
} // 开发者服务器返回一个自定义登录态(token)
message LoginResponse {
string access_token = 1;
int32 expires_in = 2; // 按 oauth2 约定走
} service AuthService {
rpc Login (LoginRequest) returns (LoginResponse);
}

使用 gRPC-Gateway 暴露 RESTful JSON API

auth.yaml 定义

type: google.api.Service
config_version: 3 http:
rules:
- selector: auth.v1.AuthService.Login
post: /v1/auth/login
body: "*"

根据配置生成代码

使用 gen.sh 生成 gRPC-Gateway 相关代码

PROTO_PATH=./auth/api
GO_OUT_PATH=./auth/api/gen/v1 protoc -I=$PROTO_PATH --go_out=paths=source_relative:$GO_OUT_PATH auth.proto
protoc -I=$PROTO_PATH --go-grpc_out=paths=source_relative:$GO_OUT_PATH auth.proto
protoc -I=$PROTO_PATH --grpc-gateway_out=paths=source_relative,grpc_api_configuration=$PROTO_PATH/auth.yaml:$GO_OUT_PATH auth.proto

运行:

sh gen.sh

成功后,会生成 auth.pb.goauth_grpc.pb.goauth.pb.gw.go 文件,代码结构如下:

├── auth
│   ├── api
│   ├── ├── gen
│   ├── ├── ├── v1
│   ├── ├── ├── ├── auth.pb.go // 生成的 golang 相关的 protobuf 代码
│   ├── ├── ├── ├── auth_grpc.pb.go // 生成 golang 相关的 gRPC Server 代码
│   ├── ├── ├── ├── auth.pb.gw.go // 生成 golang 相关的 gRPC-Gateway 代码
│   │   ├── auth.proto
│   │   └── auth.yaml
│   ├── auth
│   │   └── auth.go
│   ├── wechat
│   └── main.go
├── gateway
│   └── main.go
├── gen.sh
└── go.mod

整理一下包:

go mod tidy

初步实现 Auth gRPC Service Server

实现 AuthServiceServer 接口

我们查看生成 auth_grpc.pb.go 代码,找到 AuthServiceServer 定义:

……
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility
type AuthServiceServer interface {
Login(context.Context, *LoginRequest) (*LoginResponse, error)
mustEmbedUnimplementedAuthServiceServer()
}
……

我们在 auth/auth/auth.go 进行它的实现:

关键代码解读:

// 定义 Service 结构体
type Service struct {
Logger *zap.Logger
OpenIDResolver OpenIDResolver
authpb.UnimplementedAuthServiceServer
}
// 这里作为使用者来说做一个抽象
// 定义与微信第三方服务器通信的接口
type OpenIDResolver interface {
Resolve(code string) (string, error)
}
// 具体的方法实现
func (s *Service) Login(c context.Context, req *authpb.LoginRequest) (*authpb.LoginResponse, error) {
s.Logger.Info("received code",
zap.String("code", req.Code))
// 调用微信服务器,拿到用户的唯一标识 openId
openID, err := s.OpenIDResolver.Resolve(req.Code)
if err != nil {
return nil, status.Errorf(codes.Unavailable,
"cannot resolve openid: %v", err)
}
// 调试代码,先这样写
return &authpb.LoginResponse{
AccessToken: "token for open id " + openID,
ExpiresIn: 7200,
}, nil
}

这里有一个非常重要的编程理念,用好可以事半功倍。接口定义由使用者定义而不是实现者,如这里的 OpenIDResolver 接口。

实现 OpenIDResolver 接口

这里用到了社区的一个第三方库,这里主要用来完成开发者服务器向微信服务器换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识 UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key

当然,不用这个库,自己写也挺简单。

go get -u github.com/medivhzhan/weapp/v2

我们在 auth/wechat/wechat.go 进行它的实现:

关键代码解读:

// 相同的 Service 实现套路再来一遍
// AppID & AppSecret 要可配置,是从外面传进来的
type Service struct {
AppID string
AppSecret string
}
func (s *Service) Resolve(code string) (string, error) {
resp, err := weapp.Login(s.AppID, s.AppSecret, code)
if err != nil {
return "", fmt.Errorf("weapp.Login: %v", err)
}
if err = resp.GetResponseError(); err != nil {
return "", fmt.Errorf("weapp response error: %v", err)
}
return resp.OpenID, nil
}

配置 Auth Service gRPC Server

auth/main.go

func main() {
logger, err := zap.NewDevelopment()
if err != nil {
log.Fatalf("cannot create logger: %v", err)
}
// 配置服务器监听端口
lis, err := net.Listen("tcp", ":8081")
if err != nil {
logger.Fatal("cannot listen", zap.Error(err))
} // 新建 gRPC server
s := grpc.NewServer()
// 配置具体 Service
authpb.RegisterAuthServiceServer(s, &auth.Service{
OpenIDResolver: &wechat.Service{
AppID: "your-app-id",
AppSecret: "your-app-secret",
},
Logger: logger,
})
// 对外开始服务
err = s.Serve(lis)
if err != nil {
logger.Fatal("cannot server", zap.Error(err))
}
}

初步实现 API Gateway

gateway/main.go

// 创建一个可取消的上下文(如:请求发到一半可随时取消)
c := context.Background()
c, cancel := context.WithCancel(c)
defer cancel() mux := runtime.NewServeMux(runtime.WithMarshalerOption(
runtime.MIMEWildcard,
&runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseEnumNumbers: true, // 枚举字段的值使用数字
UseProtoNames: true,
// 传给 clients 的 json key 使用下划线 `_`
// AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
// 这里说明应使用 access_token
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true, // 忽略 client 发送的不存在的 poroto 字段
},
},
))
err := authpb.RegisterAuthServiceHandlerFromEndpoint(
c,
mux,
"localhost:8081",
[]grpc.DialOption{grpc.WithInsecure()},
)
if err != nil {
log.Fatalf("cannot register auth service: %v", err)
} err = http.ListenAndServe(":8080", mux)
if err != nil {
log.Fatalf("cannot listen and server: %v", err)
}

测试

// 发送 res.code 到后台换取 openId, sessionKey, unionId
wx.request({
url: "http://localhost:8080/v1/auth/login",
method: "POST",
data: { code: res.code },
success: console.log,
fail: console.error,
})

Refs

我是为少
微信:uuhells123
公众号:黑客下午茶
加我微信(互相学习交流),关注公众号(获取更多学习资料~)

Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第一篇(内附开发 demo)的更多相关文章

  1. Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(五):鉴权 gRPC-Interceptor 拦截器实战

    拦截器(gRPC-Interceptor)类似于 Gin 中间件(Middleware),让你在真正调用 RPC 服务前,进行身份认证.参数校验.限流等通用操作. 系列 云原生 API 网关,gRPC ...

  2. Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(四):客户端强类型约束,自动生成 API TS 类型定义

    系列 云原生 API 网关,gRPC-Gateway V2 初探 Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第一篇 Go + gRPC-Gateway(V2) ...

  3. Go+gRPC-Gateway(V2) 微服务实战,小程序登录鉴权服务(六):客户端基础库 TS 实战

    小程序登录鉴权服务,客户端底层 SDK,登录鉴权.业务请求.鉴权重试模块 Typescript 实战. 系列 云原生 API 网关,gRPC-Gateway V2 初探 Go + gRPC-Gatew ...

  4. 微服务实战系列--Nginx官网发布(转)

    这是Nginx官网写的一个系列,共七篇文章,如下 Introduction to Microservices (this article) Building Microservices: Using ...

  5. Chris Richardson微服务实战系列

    微服务实战(一):微服务架构的优势与不足 微服务实战(二):使用API Gateway 微服务实战(三):深入微服务架构的进程间通信 微服务实战(四):服务发现的可行方案以及实践案例 微服务实践(五) ...

  6. 微服务实战(六):选择微服务部署策略 - DockOne.io

    原文:微服务实战(六):选择微服务部署策略 - DockOne.io [编者的话]这篇博客是用微服务建应用的第六篇,第一篇介绍了微服务架构模板,并且讨论了使用微服务的优缺点.随后的文章讨论了微服务不同 ...

  7. 微服务实战(三):深入微服务架构的进程间通信 - DockOne.io

    原文:微服务实战(三):深入微服务架构的进程间通信 - DockOne.io [编者的话]这是采用微服务架构创建自己应用系列第三篇文章.第一篇介绍了微服务架构模式,和单体式模式进行了比较,并且讨论了使 ...

  8. go-zero微服务实战系列(十一、大结局)

    本篇是整个系列的最后一篇了,本来打算在系列的最后一两篇写一下关于k8s部署相关的内容,在构思的过程中觉得自己对k8s知识的掌握还很不足,在自己没有理解掌握的前提下我觉得也很难写出自己满意的文章,大家看 ...

  9. go-zero微服务实战系列(三、API定义和表结构设计)

    前两篇文章分别介绍了本系列文章的背景以及根据业务职能对商城系统做了服务的拆分,其中每个服务又可分为如下三类: api服务 - BFF层,对外提供HTTP接口 rpc服务 - 内部依赖的微服务,实现单一 ...

随机推荐

  1. how to convert a number to a number array in javascript without convert number to a string

    how to convert a number to a number array in javascript without convert number to a string 如何在不将数字转换 ...

  2. webpack async load modules & dynamic code splitting

    webpack async load modules & dynamic code splitting webpack 按需/异步加载/Code Splitting webpack loade ...

  3. React Hooks & useCallback & useMemo

    React Hooks & useCallback & useMemo https://reactjs.org/docs/hooks-reference.html#usecallbac ...

  4. CNN专访灵石CTO:Baccarat流动性挖矿能否持续?

    近日,CNN记者Robert独家专访Baccarat的项目团队CTO STEPHEN LITAN,跟他特别聊了聊DeFi的近况. 以下是专访全文: Robert:推出Baccarat的契机是什么? S ...

  5. PAUL ADAMS ARCHITECT:澳大利亚楼市保持涨势

    澳大利亚最新房价变化显示,住宅价格指数连续第10周上涨,包括五个主要首府城市的上涨了0.29%. 12月截至24日,布里斯班以1.03%涨幅领跑,五个首府城市平均涨幅0.78%. 在过去3个月里,悉尼 ...

  6. NGK” 呼叫河马 “智能合约火爆全网

    最近有一款基于NGK.IO公链上的智能合约"呼叫河马"在区块链市场很火.通过访问和查阅资料可知,"呼叫河马"是一款全新的智能合约Dapp小游戏,智能合约代码是1 ...

  7. 按键显示器(判断键盘监听器获得的值为普通Key还中modifiers)

    1 import sys 2 from PyQt5 import QtWidgets,QtCore 3 from PyQt5.QtCore import Qt 4 from PyQt5.uic.pro ...

  8. HTML认知

    <!DOCTYPE html>的作用 1.定义 DOCTYPE是一种标准通用标记语言的文档类型的声明,目的是告诉标准通用标记语言解析器,该用什么方式解析这个文档. <!DOCTYPE ...

  9. Navicat premium对数据库的结构同步和数据同步功能

    一.在目标数据库新建一个相同的数据库名. 二.工具-->结构同步. 三.填写源数据库和目标数据库. 四.点击比对 五.点击部署 六.点击运行 七.点击关闭.此时源数据库的结构已经同步到目标数据库 ...

  10. Docker私有仓库的搭建与使用

    目录 Docker搭建私有仓库 一.搭建registry私有仓库 1.拉取私有仓库registry镜像 2.启动私有仓库容器 3.修改deamon.json 4.重启docker 5.启动regist ...