如何使用Neo4j GraphQL Library(五)

https://neo4j.com/graphacademy/training-graphql-apis/04-graphql-apis-auth/

设置权限:
1)仅授权用户可以使用数据
2)顾客只能使用自己的数据
3)管理员可以使用所有数据

1. @auth 指令说明

定义GraphQL Type时可以通过@auth指令添加权限规则。
@auth使用JWTs做授权验证。

参考:
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。

JWT是令牌Token的编码标准,可用于许多事物,其中包括持有人令牌Bearer Token
格式:Authorization: Bearer
实例:

POST / HTTP/1.1
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJ1c2VyX2FkbWluIiwicG9zdF9hZG1pbiIsImdyb3VwX2FkbWluIl19.IY0LWqgHcjEtOsOw60mqKazhuRFKroSXFQkpCtWpgQI
content-type: application/json

简单说,用户登录后将其权限体现到Token中,用户之后的每次访问都带着这个Token,系统通过Token可以识别该用户的权限,用户可以做权限范围内的操作。当然,Token是有时效的。
为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

2. 设置签名密钥

Neo4j GraphQL Library启用授权,需要做相应的设置。

无需本地环境,使用在线沙盒环境即可:

1)neo4js andbox(neo4j graph database、neo4j browers)
https://sandbox.neo4j.com/

2)code sandbox(graphql、neo4j graphql library、apollo)
https://codesandbox.io/s/github/johnymontana/training-v3/tree/master/modules/graphql-apis/supplemental/code/04-graphql-apis-auth/begin?file=/.env

解码和验证Token需要的签名密钥
示例:dFt8QaYykR6PauvxcyKVXKauxvQuWQTc

在index.js文件中更新,添加了代码片段(设置密钥):config:{……}

const neoSchema = new Neo4jGraphQL({
  typeDefs,
  config: {
    jwt: {
      secret: "dFt8QaYykR6PauvxcyKVXKauxvQuWQTc"
    }
  }
});

如果codesandbox沙盒中已经添加了,就不用再添加了;
只需要在.env环境中配置neo4j数据库连接即可。

3. 定义两个测试用的Token

1)普通顾客的:可以访问自己的数据
用户名:EmilEifrem7474
角色:customer

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFbWlsRWlmcmVtNzQ3NCIsInJvbGVzIjpbImN1c3RvbWVyIl0sImlhdCI6MTUxNjIzOTAyMn0.YwftAMDTw6GqmYOFLGHC_f6UiUhfrJAGkZGfrGmiQ2U

2)管理员的:可以操作所有数据
用户名:BobLoblaw7687
角色:admin

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCb2JMb2JsYXc3Njg3Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.f2GKIu31gz39fMJwj5_byFCMDPDy3ncdWOIhhqcwBxk

https://jwt.io/
可以通过jwt.io解码这两个Token,如下:

52603.JPG

52604.JPG

4. 使用@auth添加权限规则:isAuthenticated

isAuthenticated ,最简单的授权
表示只要是注册用户都可以访问。当需要增加注册量时,可以用该授权。

schema.graphql 文件中添加权限规则

extend type Subject @auth(rules: [{isAuthenticated: true}])

代码解释:表示所有注册用户都可以访问题材Suject数据

查询Suject数据

{
  subjects {
    name
  }
}

报错:
Neo4jGraphQLAuthenticationError: Unauthenticated

添加该规则前,所有用户可以查询Subject信息
添加该规则后,只有注册用户可以查询Subject信息

在Http Headers中添加普通顾客的JWT Token,模拟注册用户访问,则可以查询subject数据


52601.JPG
52602.JPG

普通顾客的Token添加到HTTP HEADERS

{
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFbWlsRWlmcmVtNzQ3NCIsInJvbGVzIjpbImN1c3RvbWVyIl0sImlhdCI6MTUxNjIzOTAyMn0.YwftAMDTw6GqmYOFLGHC_f6UiUhfrJAGkZGfrGmiQ2U"
}

管理员的Token添加到HTTP HEADERS

{
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCb2JMb2JsYXc3Njg3Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.f2GKIu31gz39fMJwj5_byFCMDPDy3ncdWOIhhqcwBxk"
}

5. 使用@auth添加权限规则:operations & rules

使admin角色可以创建/修改/删除书籍信息
在schema.graphql文件中添加权限规则

extend type Book @auth(rules: [{operations: [CREATE, UPDATE, DELETE], roles: ["admin"]}])

代码解释:
@auth(rules:[具体的权限规则]):表示添加权限规则
CREATE, UPDATE, DELETE:创建、修改、删除权限,只有管理员有这三个权限(该设置不影响其他类型用户读取Book信息)

添加Book信息

mutation {
  createBooks(
    input: {
      title: "Graph Databases"
      isbn: "1491930896"
      subjects: { connect: { where: { name: "Neo4j" } } }
    }
  ) {
    books {
      title
      subjects {
        name
      }
    }
  }
}

报错:
Neo4jGraphQLAuthenticationError: Unauthenticated

如果将admin管理员的Token添加到Http Headers中,则可以添加Book信息


52605.JPG

6. 使用@auth添加权限规则:Allow

顾客只能访问自己的订单Order信息,设置权限,在schema.graphql文件中添加:

extend type Order @auth(rules: [{allow: {customer: {username: "$jwt.sub"}}}])

代码解释:
$jwt.sub:表示从JWT Token中取 sub属性,该属性对应于顾客的username
规则含义:允许当前用户访问自己的Order信息

参考:
https://neo4j.com/docs/graphql-manual/current/auth/authorization/allow/

当我们以顾客身份访问订单信息

{
    orders {
    orderID
    books {
      title
    }
  }
}

报错:
Neo4jGraphQLForbiddenError: Forbidden

当我们添加条件,只访问自己的订单信息

{
    orders(where: {
    customer: {
      username: "EmilEifrem7474"
    }
  }) {
    orderID
    books {
      title
    }
  }
}

成功,无报错

如果我们不仅仅允许顾客访问自己的订单信息,还允许管理员访问所有订单信息,则规则修改如下:
在schema.graphql文件中修改

extend type Order @auth(rules: [{allow: {customer: {username: "$jwt.sub"}}}, {roles: ["admin"]}])

7. 使用@auth添加权限规则:Where

对比这两条规则,都是只允许顾客访问自己的订单信息

extend type Order @auth(rules: [{allow: {customer: {username: "$jwt.sub"}}}])

extend type Customer @auth(rules: [{where: {username: "$jwt.sub"}}])

后一条规则对应的查询

{
    customers {
    username
    orders {
      orderID
      books {
        title
      }
    }
  }
}

代码解释:
该查询只会返回当前顾客对应的订单ID和书籍信息,代码中没有编写逻辑:username = "xxxxx";
而前一条规则对应的查询中添加了硬逻辑:username = "xxxxx";

8. 使用@auth添加权限规则:operations & bind)

参考:
https://neo4j.com/docs/graphql-manual/current/auth/authorization/bind/

在schema.graphql中添加权限规则:

extend type Review @auth(rules: [{operations: [CREATE,UPDATE], bind: {author: {username: "$jwt.sub"} }}])

顾客添加评价Review信息时,该Review的authon只能是自己,而不能是其他用户

添加评价信息的代码

mutation {
  createReviews(
    input: {
      rating: 1
      text: "Borrring"
      book: { connect: { where: { title: "Ross Poldark" } } }
      author: { connect: { where: { username: "BookLover123" } } }
    }
  ) {
    reviews {
      text
      rating
      book {
        title
      }
    }
  }
}

代码解释:
author 对应的username是"BookLover123',而不是Token中的"EmilEifrem7474",所以执行报错:
Neo4jGraphQLForbiddenError: Forbidden

如果将其中的username改成"EmilEifrem7474",与Token中一致,则执行通过

备注:这里有一点容易误解,写评价Review的是Type Customer,不是Type Author。在schema.graphql文件中Type Review的定义:

type Review {
  rating: Int
  text: String
  createdAt: DateTime @timestamp
  book: Book @relationship(type: "REVIEWS", direction: OUT)
  author: Customer @relationship(type: "WROTE", direction: IN)
}

9. 使用@auth添加权限规则:将权限添加到Cypher语句中

在schema.graphq文件中添加:

extend type Query {
 booksForCurrentUser: [Book] @auth(rules: [{ isAuthenticated: true }]) @cypher(statement: """
 MATCH (c:Customer {username: $auth.jwt.sub})-[:PLACED]->(:Order)-[:CONTAINS]->(b:Book)
 MATCH (b)-[:ABOUT]->(s:Subject)<-[:ABOUT]-(rec:Book)
 WITH rec, COUNT(*) AS score ORDER BY score DESC
 RETURN rec
 """)
}

代码解释:
@auth(rules:[……]),定义了注册用户可以使用Book信息
@cypher(statement """……"""),查询并返回与当前用户订单书籍指向同一题材的书籍信息rec

首先,查询所有书籍的title和subject

{
  books {
    title
    subjects {
      name
    }
  }
}

结果如下:

{
  "data": {
    "books": [
      {
        "title": "Graph Algorithms",
        "subjects": [
          {
            "name": "Non-fiction"
          },
          {
            "name": "Neo4j"
          },
          {
            "name": "Graph theory"
          }
        ]
      },
      {
        "title": "Inspired",
        "subjects": [
          {
            "name": "Non-fiction"
          },
          {
            "name": "Design"
          },
          {
            "name": "Product management"
          }
        ]
      },
      {
        "title": "Ross Poldark",
        "subjects": [
          {
            "name": "Cornwall"
          },
          {
            "name": "Historical fiction"
          }
        ]
      },
      {
        "title": "Graph Databases",
        "subjects": [
          {
            "name": "Neo4j"
          }
        ]
      }
    ]
  }
}

然后,再查询出当前用户订单书籍信息

{
  customers {
    orders{
      orderID
      books {
        title
        subjects {
          name
        }
      }      
    }
  }
}

查询结果

{
  "data": {
    "customers": [
      {
        "orders": [
          {
            "orderID": "9f08e841-0325-413e-b3da-43f156bd8724",
            "books": [
              {
                "title": "Graph Algorithms",
                "subjects": [
                  {
                    "name": "Non-fiction"
                  },
                  {
                    "name": "Neo4j"
                  },
                  {
                    "name": "Graph theory"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

最后,使用该查询函数booksForCurrentUser获取规则定义的信息

{
  booksForCurrentUser {
    title
  }
}
{
  "data": {
    "booksForCurrentUser": [
      {
        "title": "Inspired"
      },
      {
        "title": "Graph Databases"
      }
    ]
  }
}

该功能有点类似于猜你喜欢,比如顾客购买的书籍属于Non-fiction题材,则系统会推荐该题材下的其他书籍给顾客。

10. 练习

基于该codesandbox(已经定义好了权限,只需配置neo4j数据库连接)
https://codesandbox.io/s/github/johnymontana/training-v3/tree/master/modules/graphql-apis/supplemental/code/04-graphql-apis-auth/end?file=/.env

11. 测试

q1.JPG
q2.JPG
q3.JPG

你可能感兴趣的:(如何使用Neo4j GraphQL Library(五))