2 Star 5 Fork 1

Warlock / dgraph_graphql_module

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
6-认证和鉴权.md 7.81 KB
一键复制 编辑 原始数据 按行查看 历史
zhilong 提交于 2020-12-02 17:38 . add friends

authentication 和 authorization

  • 概念:

    大家想一下,如果graphql可以通过节点来访问数据的话,那我如果加了peter为friend,那我岂不是可以通过以下查询来获得peter的todo:

    query {
    	queryTodo() {
    	    content  # 这里的cotent依旧是我本人的
    	    done
    	    owner {
    	       name  # 这里的owner的name依旧是我本人
    	       friend(filter:{name:{eq:"peter"}}) {  # 注意,这里开始就不对劲了,想要查询peter的信息了
    	       	  name
    	       	  todo {
    	       	      content 
    	       	  }
    	       }
    	    }
    	}
    }

    如果不做鉴权,不做jwt认证,不做任何限制,那么,上面的查询语句是可以返回预期结果的,任何人都可以通过自己写的query把它post过来获取所有信息,这是我们所不容许的,所以有了验证身份和鉴权这个需求;

    authentication是身份认证的意思,就是认证用户的身份; authorization是授权的意思,就是认证用户,用户登陆进来之后,他能获得那些数据,哪些数据是他不能拿到的; 从dgraph的v20.11.x版本开始,type User @auth@secret这两个directive才可以并存,之前的版本会出错;

  • 实现:

    这里使用的后台是go语言写的jwt鉴权,机制明白了,可以用其他任何编程语言做jwt鉴权!

    dgraph有鉴权机制,它的原理是,用户登录,先通过提交用户名和密码到我们自己做的后台(比如用go语言做一个后台),审核了账号密码的正确性后,返回的一个jwt的token,这个token是经过加密的,在go中,是通过"github.com/dgrijalva/jwt-go"库进行jwt的认证开发的,它需要一个secret_key作为密钥,生成一个HS256算法的token(token是一个很长的加密字符串,没有secret_key是无法逆向解出这个token含有的信息的),然后将这个token返回给前端保存起来,前端就可以拿着这个token去dgraph用graphql访问需要权限的数据了,具体步骤如下:

    • 生成token:

      package services
      
      import (
      	"errors"
      	"time"
      
      	"github.com/dgrijalva/jwt-go"
      )
      
      const JWT_SECRETKEY = "my_secret_abcd"
      
      type UserData struct {
      	UserName string   `json:"user_name"`
      	UserRole []string `json:"user_role"`
      }
      
      type CustomClaims struct {
      	UserData `json:"jwt_data"`
      	jwt.StandardClaims
      }
      
      type JWT struct {
      	SigningKey []byte
      }
      
      var (
      	TokenExpired     = errors.New("Token is expired")
      	TokenNotValidYet = errors.New("Token not active yet")
      	TokenMalformed   = errors.New("That's not even a token")
      	TokenInvalid     = errors.New("Couldn't handle this token:")
      )
      
      // 创建一个token
      func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
      	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
      	return token.SignedString(j.SigningKey)
      }
      
      // 更新token
      func (j *JWT) RefreshToken(tokenString string) (string, error) {
      	jwt.TimeFunc = func() time.Time {
      		return time.Unix(0, 0)
      	}
      	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
      		return j.SigningKey, nil
      	})
      	if err != nil {
      		return "", err
      	}
      	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
      		jwt.TimeFunc = time.Now
      		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
      		return j.CreateToken(*claims)
      	}
      	return "", TokenInvalid
      }
      
      var myJwt JWT = JWT{
      	SigningKey: []byte(JWT_SECRETKEY),
      }
      
      

      这里定义了CustomClaimsJWT这两个结构体,前者是储存token的解码后的信息的结构体,后者的SigningKey就是我们上面说的secret_key。我们生成了token后,只要告诉dgraph我们的secret_key和我们的加密算法(这里是HS256),那么dgraph就能解出CustomClaims的user_name和user_role这些信息,go语言定义好这些结构体后,还要调用,这里调用就不细说了,反正最后就是生成一个token返回前端保存。
      这里给出一段go的生成token的代码:

      	theUser := UserData{
      		UserName: "zhilong",
              UserRole: []string{"admin"},
      	}
      
      	// 创建token
      	claims := CustomClaims{
      		UserData: theUser,
      		StandardClaims: jwt.StandardClaims{
      			NotBefore: time.Now().Unix() - 1000,       // 签名生效时间
      			ExpiresAt: time.Now().Unix() + 60*60*24*7, // 过期时间 一周
      			Issuer:    "sherlock",                     // 签名的发行者
      		},
      	}
      	token, err := myJwt.CreateToken(claims)

      这样生成的token,解析出来大概是这样:

      {
        "jwt_data": {
          "user_name": "zhilong",
          "user_role": [
            "admin"
          ]
        },
        "exp": 1606918541,
        "iss": "sherlock",
        "nbf": 1606312741
      }

    • 定义dgraph的schema时,要在最后面加上一行定义,比如:

      type A @auth(...) {
          ...
      }
      
      type B @auth(...) {
          ...
      }
      
      # Dgraph.Authorization {"VerificationKey":"","Header":"","Namespace":"","Algo":""}

      这最后一行的注释的各字段的意义是: VerificationKey:就是告诉dgraph我们上一步说的那个secret_key是什么,dgraph要用这个来解出token的信息;

      Header:告诉dgraph我前端的请求的header中,哪个header字段是带有token的,它好取出来;

      Namespace:就是包含data的字段,如上面的"jwt_data";

      Algo:就是算法类型,如上面的“HS256”;

      一个完整的示例:

      type A @auth(...){
          ...
      }
      
      # Dgraph.Authorization {"VerificationKey":"my_secret_abcd","Header":"X-Todo-App-Auth","Namespace":"jwt_data","Algo":"HS256"}
      

      这样,只要我们的每个request都带上X-Todo-App-Auth:token值这个头部信息,就能验证用户的身份和权限了;

  • schema的设计: 要限制用户能查看或不能查看哪些内容,或者谁能增添数据,谁能删除数据,都要在@auth的rule中,以下给出几个示例:

    • 基于用户角色的访问权限控制:

      type User @auth(
          delete: { rule:  "{$ROLE: { eq: \"ADMIN\" } }"}
      ) { 
          username: String! @id
          todos: [Todo]
      }

      以上的User的定义,限制了能删除user节点的只有role为"ADMIN"的用户,而$ROLE是token中解析出来的变量名,也就是说,带着这个token的query要删除某user,dgraph在解析了token后,得到该用户的$ROLE是“ADMIN”的话,就可以删,否则不行;

    • and , or & not:

      type Todo @auth(
          delete: { or: [ 
              { rule:  "query ($USER: String!) { ... }" }, # you are the author graph query
              { rule:  "{$ROLE: { eq: \"ADMIN\" } }" }
          ]}
      )

      还可以有多个不同条件的rule用and,or连在一起,或者用{not: {rule: "..."}}来表示非逻辑;

    • add:

      type Todo @auth(
          add: { rule: """
              query ($USER: String!) { 
                  queryTodo {
                      owner(filter: { username: { eq: $USER } } ) { 
                          username
                      } 
                  } 
              }"""
          }
      ){
          id: ID!
          text: String!
          owner: User
      }
      type User {
          username: String! @id
          todos: [Todo]
      }

      上例是add的规则设计,你只能添加自己的todo,而不能添加别人的;

    • update: 这个update的rule在目前dgraph团队还在开发中,仍然没有文档出来;

Go
1
https://gitee.com/wisgon/dgraph_graphql_module.git
git@gitee.com:wisgon/dgraph_graphql_module.git
wisgon
dgraph_graphql_module
dgraph_graphql_module
master

搜索帮助