prisma反向代理
概要
接触 prisma 有段时间了, 期间也使用过其他几种 graphql 接口自动生成的框架. 总的来说, 还是 prisma 生成的接口比较丰富, 使用上也比较方便, 和数据库之间耦合也低.
prisma 文档: https://www.prisma.io/docs (写本文时是 1.34 版)
为什么要做 prisma 的反向代理
prisma 服务虽然自动生成了接口, 但是这些接口其实不建议直接暴露给前端来用, 因为实际项目中, 最基本的要对接口进行认证和权限控制. 甚至还有其他需求, 不可能只用自动生成的接口就能完成所有的功能.
所以, 一般在使用 prisma 服务的时候, 一般都会再封装一层(可以称为 gateway), 在 gateway 上做认证, 权限等等, 只有合法的请求才会最终转发到 prisma 服务上. prisma 服务本身可以导出 client SDK, 用来方便 gateway 的编写, 目前支持 4 种格式 (javascript, typescript, golang, flow), javascript 和 typescript 的是 client SDK 功能比较全, golang 功能弱一些, flow 没有尝试过.
我在使用 golang client SDK 写 gateway 的时候, 发现 golang 的 graphql server 相关的库没有 js/ts 那么完善. 于是, 就想用反向代理的方式, 拦截前端的 graphql 请求, 做了相应操作之后直接再将请求内容转发给 prisma 服务. 这种方式不使用 prisma 生成的 client SDK, 也突破语言的限制, 除了 golang, java, C# 等其他语言也可以作为 prisma 的 gateway
反向代理示例(by golang)
采用 golang 的 gin 作为 gateway 的 web 服务框架. 认证部分使用 gin-jwt 中间件. 反向代理和权限部分没有使用现成的框架.
整个 gateway 的示例包含:
- prisma 服务(prisma + mysql): 这部分有现成的 docker image, 只要配置示例的表和字段即可
- gateway (golang gin): golang gin 的 api 服务
prisma 服务
prisma.yml
endpoint: http://${env:PRISMA_HOST}:${env:PRISMA_PORT}/illuminant/${env:PRISMA_STAGE}
datamodel: datamodel.prisma secret: ${env:PRISMA_MANAGEMENT_API_SECRET} generate:
- generator: go-client
output: ./
.env
PRISMA_HOST=localhost
PRISMA_PORT=4466
PRISMA_STAGE=dev
PRISMA_MANAGEMENT_API_SECRET=secret-key
datamodel.prisma
type User {
id: ID! @id
name: String! @unique
realName: String!
password: String! createdAt: DateTime! @createdAt
updatedAt: DateTime! @updatedAt
}
docker-compose.yml
version: '3'
services:
illuminant:
image: prismagraphql/prisma:1.34
# restart: always
ports:
- "4466:4466"
environment:
PRISMA_CONFIG: |
port: 4466
managementApiSecret: secret-key
databases:
default:
connector: mysql
host: mysql-db
user: root
password: prisma
# rawAccess: true
port: 3306
migrations: true mysql-db:
image: mysql:5.7
# restart: always
environment:
MYSQL_ROOT_PASSWORD: prisma
volumes:
- mysql:/var/lib/mysql
volumes:
mysql: ~
以上文件放在同一个目录即可, 包含了所有 prisma 服务和 mysql 服务所需要的文件
gateway 服务
gateway 服务是关键, 也是今后扩展的部分. 采用 golang gin 框架来编写.
整体流程
- HTTP 请求
- route 路由
- 认证 Check
- 权限 Check
- 请求转发 prisma 服务(这一步一般都是转发到 prisma, 如果有上传/下载, 或者统计之类的需求, 需要另外写 API)
- 返回 Response
认证
authMiddleware := controller.JwtMiddleware()
apiV1 := r.Group("/api/v1")
// no auth routes
apiV1.POST("/login", authMiddleware.LoginHandler)
// auth routes
authRoute := apiV1.Group("/")
authRoute.GET("/refresh_token", authMiddleware.RefreshHandler)
authRoute.Use(authMiddleware.MiddlewareFunc())
{
// proxy prisma graphql
authRoute.POST("/graphql", ReverseProxy())
}
/api/v1/graphql 在满足 jwt 认证的情况下才可以访问.
反向代理
func ReverseProxy() gin.HandlerFunc {
return func(c *gin.Context) {
director := func(req *http.Request) {
req.URL.Scheme = "http"
req.URL.Host = primsa-host
req.URL.Path = primsa-endpoint
delete(req.Header, "Authorization")
req.Header["Authorization"] = []string{"Bearer " + primsa-token}
}
// 解析出 body 中的内容, 进行权限检查
body, err := c.GetRawData()
if err != nil {
fmt.Println(err)
}
// 对 body 进行权限 check
// 权限 Check, 解析出 graphql 中请求的函数, 然后判断是否有权限
// 目前的方式是根据请求中函数的名称来判断权限, 也就是只能对表的 CURD 权限进行判断, 对于表中的字段权限还无法检查
// 如果权限检查没有通过, 直接返回, 不要再进行下面的请求转发
// 将 body 反序列化回请求中, 转发给 prisma 服务
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
proxy := &httputil.ReverseProxy{Director: director}
proxy.ModifyResponse = controller.RewriteBody
proxy.ServeHTTP(c.Writer, c.Request)
}
}
权限
// 检查权限
func CheckAuthority(body []byte, userId string) bool {
var bodyJson struct {
Query string `json:"query"`
}
log := logger.GetLogger()
if err := json.Unmarshal(body, &bodyJson); err != nil {
log.Error("body convert to json error: %s", err.Error())
return false
}
graphqlFunc := RegrexGraphqlFunc(bodyJson.Query)
if graphqlFunc == "" {
return false
}
// 这里的 userId 是从 jwt 中解析出来的, 然后再判断用户是否有权限
if graphqlFunc == "users" {
return false
}
return true
}
// 匹配 graphql 请求的函数
func RegrexGraphqlFunc(graphqlReq string) string {
graphqlReq = strings.TrimSpace(graphqlReq)
// reg examples:
// { users {id} }
// { users(where: {}) {id} }
// mutation{ user(data: {}) {id} }
var regStrs = []string{
`^\{\s*(\w+)\s*\{.*\}\s*\}$`,
`^\{\s*(\w+)\s*\(.*\)\s*\{.*\}\s*\}$`,
`^mutation\s*\{\s*(\w+)\s*\(.*\)\s*\{.*\}\s*\}$`,
}
for _, regStr := range regStrs {
r := regexp.MustCompile(regStr)
matches := r.FindStringSubmatch(graphqlReq)
if matches != nil && len(matches) > 1 {
return matches[1]
}
}
return ""
}
这里的权限检查是个实现思路, 不是最终的代码.
其中用正则表达式的方式来匹配请求中的函数只是临时的方案, 不是最好的方式,
最好的方式应该用 golang 对应的 graphql 解析库来解析出请求的结构, 然后再判断解析出的函数时候有权限
总结
采用反向代理的方式, 是为了突破 prisma client SDK 的限制, 如果以后 client SDK 完善之后, 还是基于 client SDK 来开发 gateway 更加可靠.
prisma反向代理的更多相关文章
- hasura的golang反向代理
概述 反向代理代码 对请求的处理 对返回值的处理 遇到的问题 概述 一直在寻找一个好用的 graphql 服务, 之前使用比较多的是 prisma, 但是 prisma1 很久不再维护了, 而 pri ...
- nginx配置反向代理或跳转出现400问题处理记录
午休完上班后,同事说测试站点访问接口出现400 Bad Request Request Header Or Cookie Too Large提示,心想还好是测试服务器出现问题,影响不大,不过也赶紧上 ...
- 使用python自动生成docker nginx反向代理配置
由于在测试环境上用docker部署了多个应用,而且他们的端口有的相同,有的又不相同,数量也比较多,在使用jenkins发版本的时候,不好配置,于是想要写一个脚本,能在docker 容器创建.停止的时候 ...
- Windos环境用Nginx配置反向代理和负载均衡
Windos环境用Nginx配置反向代理和负载均衡 引言:在前后端分离架构下,难免会遇到跨域问题.目前的解决方案大致有JSONP,反向代理,CORS这三种方式.JSONP兼容性良好,最大的缺点是只支持 ...
- Nginx反向代理,负载均衡,redis session共享,keepalived高可用
相关知识自行搜索,直接上干货... 使用的资源: nginx主服务器一台,nginx备服务器一台,使用keepalived进行宕机切换. tomcat服务器两台,由nginx进行反向代理和负载均衡,此 ...
- 使用Nginx反向代理 让IIS和Tomcat等多个站点一起飞
使用Nginx 让IIS和Tomcat等多个站点一起飞 前言: 养成一个好习惯,解决一个什么问题之后就记下来,毕竟“好记性不如烂笔头”. 这样也能帮助更多的人 不是吗? 最近闲着没事儿瞎搞,自己在写一 ...
- 使用nginx反向代理,一个80端口下,配置多个微信项目
我们要接入微信公众号平台开发,需要填写服务器配置,然后依据接口文档才能实现业务逻辑.但是微信公众号接口只支持80接口(80端口).我们因业务需求需要在一个公众号域名下面,发布两个需要微信授权的项目,怎 ...
- 腾讯云下安装 nodejs + 实现 Nginx 反向代理
本文将介绍如何给腾讯云上的 Ubuntu Server 12.04 LTS 64位主机安装 node 及 nginx,并简单配置反向代理. 笔者在整个安装过程中遇到不少麻烦(不赘述),如果你希望少踩坑 ...
- 简易nginx TCP反向代理设置
nginx从1.9.0开始支持TCP反向代理,之前只支持HTTP.这是我的系统示意图: 为何需要? 为什么需要反向代理?主要是: 负载均衡 方便管控 比如我现在要更新后端服务器,如果不用负载均衡的话, ...
随机推荐
- Python开发GUI实战:图片转换素描画工具!
奋斗没有终点 好好学习72变,因为将来 没有人能替你阻挡81难 . 生如蝼蚁,当有鸿鹄之志: 命如纸薄,应有不屈之心 . 今天被这句话触动了,所以开篇分享给大家.鸡汤有毒,但有时大家却靠它激励自己 ...
- NRF51822/NRF51802/NRF52832/NRF52810/NRF52811/NRF52840内核对比
NRF51822的内核为M0,FLASH是256K,RAM是16K,蓝牙BLE4.0/4.2(SDK新版本支持4.2)NRF51802的内核为M0,FLASH是256K,RAM是16K,蓝牙BLE4. ...
- Java实现Mysql的 substring_index 函数功能
Java实现Mysql数据库中 substring_index函数 前言: 由于hive中没有这个 substring_index函数,所以就自定义一个udf函数来调用使用.(不通过hive使用时可以 ...
- IT兄弟连 Java语法教程 数组 数组的声明
Java语言支持两种语法格式来定义数组: type[] arrayName; type arrayName[]; 对这两种语法格式而言,通常推荐使用第一种格式,因为第一种格式不仅具有更好的语义,而且具 ...
- IT兄弟连 Java语法教程 流程控制语句 控制循环结构2
使用continue忽略本次循环剩下的语句 continue的功能和break有点类似,区别是continue只是忽略本次循环剩下的语句,接着开始下一次循环,并不会终止循环:而break则是完全终止循 ...
- IT兄弟连 Java语法教程 逻辑运算符
表8中显示的布尔逻辑运算符只能操作布尔类型的操作数,所有的二元逻辑运算符都可以组合两个布尔值,得到的结果为布尔类型. 表8 布尔逻辑运算符 布尔逻辑运算符”&“.”|“以及”^“,都会布尔值 ...
- pandas 学习 第7篇:DataFrame - 数据处理(应用、操作索引、重命名、合并)
DataFrame的这些操作和Series很相似,这里简单介绍一下. 一,应用和应用映射 apply()函数对每个轴应用一个函数,applymap()函数对每个元素应用一个函数: DataFrame. ...
- mysql 常用命令行总结
登录 mysql -h -u root -p 回车后输入密码,即可登录 直接进入某个库 -D 库名 mysql -h -u root -D account -p 列举数据库.表 show databa ...
- 使用Java操作Elasticsearch(Elasticsearch的java api使用)
1.Elasticsearch是基于Lucene开发的一个分布式全文检索框架,向Elasticsearch中存储和从Elasticsearch中查询,格式是json. 索引index,相当于数据库中的 ...
- ASP.NET Core框架深度学习(二) 管道对象
4.HttpContext 第一个对象 我们的ASP.NET Core Mini由7个核心对象构建而成.第一个就是大家非常熟悉的HttpContext对象,它可以说是ASP.NET Core应用开发中 ...