📜  GraphQL-身份验证客户端

📅  最后修改于: 2020-10-25 05:13:50             🧑  作者: Mango


验证是验证用户或进程身份的过程或动作。应用程序对用户进行身份验证很重要,以确保匿名用户无法使用该数据。在本节中,我们将学习如何认证GraphQL客户端。

捷运

在此示例中,我们将使用jQuery创建客户端应用程序。为了验证请求,我们将在服务器端使用express-jwt模块。

express-jwt模块是一个中间件,可让您使用JWT令牌对HTTP请求进行身份验证。 JSON Web令牌(JWT)是一个长字符串,用于标识已登录的用户。

用户成功登录后,服务器将生成JWT令牌。该令牌可以清楚地标识日志。换句话说,令牌是用户身份的表示。因此,下一次,当客户端访问服务器时,它必须提供此令牌以获取所需的资源。客户端可以是移动应用程序或Web应用程序。

Express JWT模块

插图

我们将按照逐步的过程来理解此插图。

设置服务器

以下是设置服务器的步骤-

步骤1-下载并安装项目的必需依赖项

创建一个文件夹auth-server-app 。从终端将目录更改为auth-server-app 。请按照“环境设置”一章中说明的步骤3到5进行操作。

第2步-创建架构

在项目文件夹auth-server-app中添加schema.graphql文件,并添加以下代码-

type Query
{
   greetingWithAuth:String
}

第3步-添加解析器

在项目文件夹中创建文件resolvers.js并添加以下代码-

解析器将验证GraphQL的上下文对象中是否有经过身份验证的用户对象。如果无法通过身份验证的用户,则会引发异常。

const db = require('./db')

const Query = {
   greetingWithAuth:(root,args,context,info) => {

      //check if the context.user is null
      if (!context.user) {
         throw new Error('Unauthorized');
      }
      return "Hello from TutorialsPoint, welcome back : "+context.user.firstName;
   }
}

module.exports = {Query}

第4步-创建Server.js文件

身份验证中间件使用JSON Web令牌对调用者进行身份验证。身份验证的URL是http:// localhost:9000 / login

这是一个后期操作。用户必须提交将通过后端验证的电子邮件和密码。如果使用jwt.sign方法生成了有效令牌,则客户端将必须在标头中发送此令牌以用于后续请求。

如果令牌有效,则将使用解码后的JSON对象设置req.user,以供以后的中间件用于授权和访问控制。

以下代码使用两个模块-jsonwebtoken和express-jwt来验证请求-

  • 当在迎接用户点击按钮,用于/ graphql路线的请求被发出。如果用户未通过身份验证,将提示他进行身份验证。

  • 向用户显示一个接受电子邮件ID和密码的表单。在我们的示例中,/ login路由负责验证用户。

  • / login路由验证是否在数据库中找到用户提供的凭据的匹配项。

  • 如果凭据无效,则将HTTP 401异常返回给用户。

  • 如果凭据有效,则服务器将生成令牌。该令牌作为响应的一部分发送给用户。这是通过jwt.sign函数完成的。

const expressJwt = require('express-jwt');
const jwt = require('jsonwebtoken');

//private key
const jwtSecret = Buffer.from('Zn8Q5tyZ/G1MHltc4F/gTkVJMlrbKiZt', 'base64');

app.post('/login', (req, res) => {
   const {email, password} = req.body;
   
   //check database
   const user = db.students.list().find((user) =>  user.email === email);
   if (!(user && user.password === password)) {
      res.sendStatus(401);
      return;
   }
   
   //generate a token based on private key, token doesn't have an expiry
   const token = jwt.sign({sub: user.id}, jwtSecret);
   res.send({token});
});

对于每个请求,都会调用app.use()函数。反过来,这将调用expressJWT中间件。该中间件将解码JSON Web令牌。存储在令牌中的用户ID将被检索并作为属性用户存储在请求对象中。

//decodes the JWT and stores in request object
app.use(expressJwt({
   secret: jwtSecret,
   credentialsRequired: false
}));

为了使GraphQL上下文中的user属性可用,该属性被分配给上下文对象,如下所示-

//Make req.user available to GraphQL context
app.use('/graphql', graphqlExpress((req) => ({
   schema,
   context: {user: req.user &&apm; db.students.get(req.user.sub)}
})));

在当前文件夹路径中创建server.js 。完整的server.js文件如下-

const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');
const expressJwt = require('express-jwt'); //auth
const jwt = require('jsonwebtoken'); //auth
const db = require('./db');

var port = process.env.PORT || 9000
const jwtSecret = Buffer.from('Zn8Q5tyZ/G1MHltc4F/gTkVJMlrbKiZt', 'base64');
const app = express();

const fs = require('fs')
const typeDefs = fs.readFileSync('./schema.graphql',{encoding:'utf-8'})
const resolvers = require('./resolvers')
const {makeExecutableSchema} = require('graphql-tools')

const schema = makeExecutableSchema({typeDefs, resolvers})

app.use(cors(), bodyParser.json(), expressJwt({
   secret: jwtSecret,
   credentialsRequired: false
}));

const  {graphiqlExpress,graphqlExpress} = require('apollo-server-express')

app.use('/graphql', graphqlExpress((req) => ({
   schema,
   context: {user: req.user && db.students.get(req.user.sub)}
})));
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))

//authenticate students
app.post('/login', (req, res) => {
   const email = req.body.email;
   const password = req.body.password;

   const user = db.students.list().find((user) =>  user.email === email);
   if (!(user && user.password === password)) {
      res.sendStatus(401);
      return;
   }
   const token = jwt.sign({sub: user.id}, jwtSecret);
   res.send({token});
});

app.listen(port, () => console.info(`Server started on port ${port}`));

第5步-运行应用程序

在终端中执行命令npm start。服务器将启动并在9000端口上运行。在这里,我们使用GraphiQL作为客户端来测试应用程序。

打开浏览器,然后输入URL http:// localhost:9000 / graphiql 。在编辑器中键入以下查询-

{
   greetingWithAuth
}

在以下响应中,我们未通过身份验证,因此出现错误。

{
   "data": {
      "greetingWithAuth": null
   },
   "errors": [
      {
         "message": "Unauthorized",
         "locations": [
            {
               "line": 2,
               "column": 3
            }
         ],
         "path": [
            "greetingWithAuth"
         ]
      }
   ]
}

在下一部分中,让我们创建一个客户端应用程序进行身份验证。

设置JQuery客户端

在客户端应用程序中,提供了一个greet按钮,该按钮将调用模式greetingWithAuth 。如果您单击按钮而不登录,它将显示以下错误消息:

客户端应用程序身份验证

使用数据库中可用的用户登录后,将出现以下屏幕:

客户端应用程序身份验证成功

要访问greeting ,我们需要首先访问URL http:// localhost:9000 / login路由,如下所示。

响应将包含从服务器生成的令牌。

$.ajax({
   url:"http://localhost:9000/login",
   contentType:"application/json",
   type:"POST",
   data:JSON.stringify({email,password}),
   success:function(response) {
      loginToken = response.token;
      $('#authStatus')
      .html("authenticated successfully")
      .css({"color":"green",'font-weight':'bold'});
      $("#greetingDiv").html('').css({'color':''});
   },
   error:(xhr,err) =>  alert('error')
})

成功登录后,我们可以访问如下所示的greetingWithAuth模式。对于所有后续带有承载令牌的请求,都应该有一个Authorizationheader。

{ 
   url: "http://localhost:9000/graphql",
   contentType: "application/json",
   headers: {"Authorization": 'bearer '+loginToken},  type:'POST',
   data: JSON.stringify({
   query:`{greetingWithAuth}`
}

以下是index.html的代码-


      
   
   
   
      

GraphQL Authentication








*Login first to access greeting