prisma Reverse Proxy

Overview

Prisma in contact for some time, the frame is also used in several other graphql interface is automatically generated during Overall, prisma or generated interface rich, more convenient use, and the coupling between the databases is also low.

prisma document: https://www.prisma.io/docs (this writing is version 1.34)

Why do prisma reverse proxy

prisma services, though the interface is automatically generated, but in fact these interfaces is not recommended to use directly exposed to the front, because the actual project, the most basic of interface to authentication and access control. There are even other needs, can not only automatically generated interface will be able to complete all of the features.

Therefore, the general when using prisma services, usually repackaging layer (may be referred to as a gateway), do authentication, permissions on the gateway, and so on, only legitimate requests will be forwarded to the final prisma service. Prisma service itself can export client SDK, to facilitate the preparation gateway currently supports four formats (javascript, typescript, golang, flow), javascript and typescript is the client SDK feature comparison, golang function weaker, flow not tried.

After I wrote when using golang client SDK gateway found golang of graphql server-related library does not have js / ts so perfect. So, wanted to use a reverse proxy mode, the front graphql intercept requests made directly to the appropriate action again prisma contents of the request is forwarded to the service. in this way not using prisma generated client SDK, breaking through language restrictions, in addition to golang, java, C #, and other languages ​​can be used as a gateway prisma

Reverse proxy example (by golang)

Using golang of Gin . Certification as part of a web service gateway framework of gin-jwt middleware reverse proxy and Permissions section do not use ready-made frame.

Entire sample gateway comprises:

  1. prisma Service (prisma + mysql): This section has ready docker image, as long as an example of the configuration tables and fields to
  2. gateway (golang gin): golang gin fire 的 服务

prisma Service

  1. 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: ./
  2. .env

    PRISMA_HOST=localhost
    PRISMA_PORT=4466
    PRISMA_STAGE=dev
    PRISMA_MANAGEMENT_API_SECRET=secret-key
  3. datamodel.prisma

    type User {
      id: ID! @id
      name: String! @unique
      realName: String!
      password: String!
    
      createdAt: DateTime! @createdAt
      updatedAt: DateTime! @updatedAt
    }
  4. 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: ~

Above file in the same directory can contain files of all prisma services and the services they need mysql

gateway services

gateway service is the key, but also the future expansion of the parts. golang gin using the framework to write.

Overall process

  1. HTTP request
  2. route route
  3. Certified Check
  4. Permissions Check
  5. Prisma request forwarding service (this step is usually forwarded to prisma, if you have upload / download, or demand statistics and the like, the need to write API)
  6. Return Response

Authenticate

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 can access while meeting jwt certification.

Reverse Proxy

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)
  }
}

Competence

// 检查权限
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 ""
}

Permission checks idea here is achieved, not the final code.
Wherein manner to the regular expression matching function request only temporary solution, not the best way,
the best way should be a corresponding graphql parsing library golang structure parsed request, and then determine the parsed functions when permission has

to sum up

Using a reverse proxy mode, in order to break prisma client SDK restrictions, if later, after improving client SDK, or gateway-based client SDK to develop more reliable.

Guess you like

Origin www.cnblogs.com/wang_yb/p/11411840.html