身份验证是验证用户或进程身份的过程或动作。应用程序对用户进行身份验证以确保数据对匿名用户不可用是很重要的。在本节中,我们将学习如何对 GraphQL 客户端进行身份验证。
在此示例中,将使用jQuery创建客户端应用程序。为了验证请求,将在服务器端使用 express-jwt 模块。
express-jwt模块是一个中间件,可让您使用JWT令牌对HTTP请求进行身份验证。 JSON Web令牌(JWT)是一个长字符串,用于标识已登录的用户。
用户成功登录后,服务器将生成JWT令牌。该令牌明确标识日志。换句话说,令牌是用户身份的表示。因此,下一次,当客户端访问服务器时,它必须提供此令牌以获取所需的资源。客户端可以是移动应用程序或Web应用程序。
链接:https://www.learnfk.comhttps://www.learnfk.com/graphql/graphql-authenticating-client.html
来源:LearnFk无涯教程网
以下是设置服务器的步骤-
创建一个文件夹 auth-server-app 。从终端将目录更改为 auth-server-app 。请按照"环境设置"一章中介绍的步骤3至5进行操作。
在项目文件夹 auth-server-app 中添加 schema.graphql 文件,并添加以下代码-
type Query { greetingWithAuth:String }
在项目文件夹中创建文件 resolvers.js 并添加以下代码-
解析器将验证GraphQL的context对象中是否有经过身份验证的用户对象。如果未通过身份验证的用户,它将引发异常。
const db=require('./db') const Query={ greetingWithAuth:(root,args,context,info) => { //检查 context.user 是否为空 if (!context.user) { throw new Error('Unauthorized'); } return "Hello from LearnFk, welcome back : "+context.user.firstName; } } module.exports={Query}
身份验证中间件使用JSON Web令牌对调用者进行身份验证。身份验证的URL为 http://localhost:9000/login 。
如果令牌有效,则将使用解码的JSON对象设置请求用户,以供以后的中间件用于授权和访问控制。
以下代码使用两个模块-jsonwebtoken和express-jwt来验证请求
const expressJwt=require('express-jwt'); const jwt=require('jsonwebtoken'); //私钥 const jwtSecret=Buffer.from('Zn8Q5tyZ/G1MHltc4F/gTkVJMlrbKiZt', 'base64'); app.post('/login', (req, res) => { const {email, password}=req.body; //检查数据库 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.use()函数。反过来,这将调用expressJWT中间件。该中间件将解码JSON Web令牌。存储在令牌中的用户ID将被检索并作为属性用户存储在请求对象中。
//解码 JWT 并存储在请求对象中 app.use(expressJwt({ secret: jwtSecret, credentialsRequired: false }));
为了使GraphQLcontext中的用户属性可用,该属性被分配给 context 对象,如下所示-
//使 req.user 可用于 GraphQL 上下文 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}`));
在终端中执行 npm start命令。服务器将启动并在9000端口上运行。在这里,无涯教程使用GraphiQL作为客户端来测试应用程序。
打开浏览器并输入URL http://localhost:9000/graphiql 。在编辑器中键入以下查询-
{ greetingWithAuth }
在以下响应中,由于未通过身份验证的用户收到了错误消息。
{ "data": { "greetingWithAuth": null }, "errors": [ { "message": "Unauthorized", "locations": [ { "line": 2, "column": 3 } ], "path": [ "greetingWithAuth" ] } ] }
在下一节中,让无涯教程创建一个客户端应用程序进行身份验证。
在客户端应用程序中,提供了一个greet按钮,它将调用架构 greetingWithAuth 。如果您单击按钮而不登录,它将显示以下错误消息:
使用数据库中可用的用户登录后,将出现以下屏幕:
要访问问候,需要首先访问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的代码-
<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script> $(document).ready(function() { let loginToken=""; $("#btnGreet").click(function() { $.ajax({url: "http://localhost:9000/graphql", contentType: "application/json", headers: {"Authorization": 'bearer '+loginToken}, type:'POST', data: JSON.stringify({ query:`{greetingWithAuth}` }), success: function(result) { $("#greetingDiv").html("<h1>"+result.data.greetingWithAuth+"</h1>") }, error:function(jQxhr,error) { if(jQxhr.status == 401) { $("#greetingDiv").html('please authenticate first!!') .css({"color":"red",'font-weight':'bold'}) return; } $("#greetingDiv").html('error').css("color","red"); } }); }); $('#btnAuthenticate').click(function() { var email= $("#txtEmail").val(); var password= $("#txtPwd").val(); if(email && password) { $.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') }) }else alert("email and pwd empty") }) }); </script> </head> <body> <h1> GraphQL Authentication </h1> <hr/> <section> <button id="btnGreet">Greet</button> <br/> <br/> <div id="greetingDiv"></div> </section> <br/> <br/> <br/> <hr/> <section id="LoginSection"> <header> <h2>*Login first to access greeting </h2> </header> <input type="text" value="mohtashim.mohammad@tutorialpoint.org" placeholder="enter email" id="txtEmail"/> <br/> <input type="password" value="pass123" placeholder="enter password" id="txtPwd"/> <br/> <input type="button" id="btnAuthenticate" value="Login"/> <p id="authStatus"></p> </section> </body> </html>
祝学习愉快!(内容编辑有误?请选中要编辑内容 -> 右键 -> 修改 -> 提交!)