经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
graphql请求中的数据保护(illuminant)
来源:cnblogs  作者:wang_yb  时间:2021/2/18 15:35:41  对本文有异议

概述

通过 graphql 请求数据时, where条件是自己写在graphql请求字符串中的, 所以获取多少数据后端无法控制, 比如

  1. {
  2. blogs(where: {user_id: {_eq: "xxxxxx"}}){
  3. id
  4. title
  5. content
  6. user_id
  7. }
  8. }

通过 where 条件, 虽然可以获取只属于自己的 blog 信息.
但是, 这样带来了2个问题:

  1. 要把 user_id (有时候是role_id) 信息暴露给前端
  2. 前端可以不加where条件, 从而获取所有blog, 包括其他用户的 blog

解决方案

为了解决上述问题, 在 illuminant 框架中实现了一个 修改请求中 where 条件的中间件.

首先, 用户的 user_id/role_id 信息可以放在 jwt-token 中(配合已有的 JWT 中间件), 不显式的在response中返回前端.
然后, 配置需要进行 user_id/role_id 的条件注入的函数(比如上面的blogs), 此中间件在执行 graphql 之前, 把包含 user_id/role_id 的where 条件注入到 graphql 请求的函数中.

image.png

Where 中间件定义

  1. type HasuraWhere struct {
  2. GQLKey string // 注入到graphql请求中where条件的key, 一般就是 user_id 或者 role_id
  3. JWTKey string // 对应到 jwt 中的key, 一般就是 user_id 或者 role_id
  4. CheckGQLFuncs []string // 需要注入 where 条件的 graphql 函数
  5. }

上面三个字段也就是 Where中间件的配置信息.

Where中间件处理流程

  1. func (hw *HasuraWhere) AddWhereCondition(c *gin.Context) {
  2. lg := logger.GetLogger()
  3. claims := jwt.ExtractClaims(c)
  4. path := c.Request.URL.Path
  5. // 如果不是 graphql 请求, 直接返回
  6. if strings.Index(path, "/api/v1/graphql") < 0 {
  7. return
  8. }
  9. // graphql api
  10. body, err := c.GetRawData()
  11. if err != nil {
  12. lg.Err(err).Msg("HasuraWhere middleware: AddWhereCondition")
  13. return
  14. }
  15. // 核心处理
  16. newBody, err := util.AddWhereCondition(body, hw.GQLKey, claims[hw.JWTKey].(string), hw.CheckGQLFuncs)
  17. if err != nil {
  18. lg.Err(err).Msg("HasuraWhere middleware: AddWhereCondition")
  19. return
  20. }
  21. c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(newBody))
  22. c.Request.ContentLength = int64(len(newBody))
  23. }

util package 下的 AddWhereCondition 包含主要处理逻辑

  1. func AddWhereCondition(body []byte, key, val string, gqlFuncs []string) ([]byte, error) {
  2. var req gqlRequest
  3. if err := json.Unmarshal(body, &req); err != nil {
  4. return nil, err
  5. }
  6. // req.Query: 请求中原始的 graphql 字符串
  7. // key: 需要注入到where条件中的 key
  8. // val: 需要注入到where条件中的 value
  9. // gqlFuncs: 需要进行注入操作的 graphql 函数
  10. newQuery := changeFields(req.Query, key, val, gqlFuncs)
  11. req.Query = newQuery
  12. return json.Marshal(&req)
  13. }

graphql 的AST解析和修改依赖 github.com/graphql-go/graphql

  1. // 函数已有where条件, 将key/val 注入到已有的where中
  2. func changeWithWhere(arg *sourceAST.Argument, key, val string) {
  3. whereValue := arg.Value.GetValue().([]*sourceAST.ObjectField)
  4. injectClause := createInjectCondition(key, val)
  5. whereValue = append(whereValue, injectClause)
  6. fmt.Printf("where value: %#v\n", whereValue)
  7. arg.Value = sourceAST.NewObjectValue(&sourceAST.ObjectValue{
  8. Fields: whereValue,
  9. })
  10. }
  11. // 函数没有where条件, 新增where 条件, 并将 key/val 注入其中
  12. func changeWithoutWhere(node *sourceAST.Field, key, val string) {
  13. injectClause := createInjectCondition(key, val)
  14. arg := sourceAST.NewArgument(&sourceAST.Argument{
  15. Name: sourceAST.NewName(&sourceAST.Name{Value: "where"}),
  16. Value: sourceAST.NewObjectValue(&sourceAST.ObjectValue{
  17. Fields: []*sourceAST.ObjectField{injectClause},
  18. }),
  19. })
  20. if node.Arguments == nil {
  21. node.Arguments = make([]*sourceAST.Argument, 0)
  22. }
  23. node.Arguments = append(node.Arguments, arg)
  24. }
  25. // 根据 key/val 创建 graphql where条件的结构
  26. func createInjectCondition(key, val string) *sourceAST.ObjectField {
  27. return sourceAST.NewObjectField(&sourceAST.ObjectField{
  28. Name: sourceAST.NewName(&sourceAST.Name{Value: key}),
  29. Value: sourceAST.NewObjectValue(&sourceAST.ObjectValue{
  30. Fields: []*sourceAST.ObjectField{
  31. sourceAST.NewObjectField(&sourceAST.ObjectField{
  32. Name: sourceAST.NewName(&sourceAST.Name{Value: "_eq"}),
  33. Value: sourceAST.NewStringValue(&sourceAST.StringValue{Value: val}),
  34. }),
  35. },
  36. }),
  37. })
  38. }

实现效果

将此中间件和JWT中间件一起在路由中使用:

  1. whereMiddleware := middleware.NewHasuraWhereMiddleware("user_id", "id", []string{"blogs", "blogs2", "blogs3"})
  2. jwtAuthRoutes := r.Use(authMiddleware.MiddlewareFunc(), whereMiddleware)
  3. // ... 省略 ...
  4. jwtAuthRoutes.POST("/graphql", util.ReverseProxy())

请求时的 graphql如下:

  1. query query_blogs($title: String!){
  2. blogs(where: {title: {_eq: "xxx"}}) {
  3. id
  4. title
  5. content
  6. user_id
  7. }
  8. }
  9. {
  10. blogs2 {
  11. id
  12. title
  13. content
  14. user_id
  15. }
  16. }
  17. query query_blogs3($offset: Int!){
  18. blogs3(offset: $offset) {
  19. id
  20. title
  21. content
  22. user_id
  23. }
  24. }
  25. {
  26. not_blogs {
  27. id
  28. title
  29. content
  30. user_id
  31. }
  32. }

经过中间件之后, 实际执行的 graphql 如下:

  1. query query_blogs($title: String!){
  2. blogs(where: {title: {_eq: "xxx"}, user_id: {_eq: "user_id_from_jwt_token"}}) {
  3. id
  4. title
  5. content
  6. user_id
  7. }
  8. }
  9. {
  10. blogs2(where: {user_id: {_eq: "user_id_from_jwt_token"}}) {
  11. id
  12. title
  13. content
  14. user_id
  15. }
  16. }
  17. query query_blogs3($offset: Int!){
  18. blogs3(offset: $offset, where: {user_id: {_eq: "user_id_from_jwt_token"}}) {
  19. id
  20. title
  21. content
  22. user_id
  23. }
  24. }
  25. {
  26. not_blogs {
  27. id
  28. title
  29. content
  30. user_id
  31. }
  32. }

相关代码

illuminant项目 中:

  • /routes/jwt.go
  • /middleware/where_middleware.go
  • /util/graphql_where.go

原文链接:http://www.cnblogs.com/wang_yb/p/14379364.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号