GraphQL - uwierzytelnianie klienta
Uwierzytelnianie to proces lub czynność polegająca na weryfikacji tożsamości użytkownika lub procesu. Ważne jest, aby aplikacja uwierzytelniała użytkownika, aby zapewnić, że dane nie są dostępne dla anonimowego użytkownika. W tej sekcji dowiemy się, jak uwierzytelnić klienta GraphQL.
Express JWT
W tym przykładzie użyjemy jQuery do stworzenia aplikacji klienckiej. Do uwierzytelniania żądań będziemy używać express-jwt moduł po stronie serwera.
Moduł express-jwt to oprogramowanie pośredniczące, które umożliwia uwierzytelnianie żądań HTTP przy użyciu tokenów JWT. JSON Web Token (JWT) to długi ciąg identyfikujący zalogowanego użytkownika.
Po pomyślnym zalogowaniu się użytkownika serwer generuje token JWT. Ten token wyraźnie identyfikuje dziennik. Innymi słowy, token reprezentuje tożsamość użytkownika. Więc następnym razem, gdy klient przyjdzie na serwer, musi przedstawić ten token, aby uzyskać wymagane zasoby. Klientem może być aplikacja mobilna lub aplikacja internetowa.
Ilustracja
Aby zrozumieć tę ilustrację, będziemy postępować według stopniowej procedury.
Konfigurowanie serwera
Poniżej przedstawiono kroki konfiguracji serwera -
Krok 1 - Pobierz i zainstaluj wymagane zależności dla projektu
Utwórz folder auth-server-app. Zmień katalog na auth-server-app z terminala. Wykonaj kroki od 3 do 5 opisane w rozdziale Konfiguracja środowiska.
Krok 2 - Utwórz schemat
Dodaj schema.graphql plik w folderze projektu auth-server-app i dodaj następujący kod -type Query
{
greetingWithAuth:String
}
Krok 3 - Dodaj resolwery
Utwórz plik resolvers.js w folderze projektu i dodaj następujący kod -
Resolver sprawdzi, czy uwierzytelniony obiekt użytkownika jest dostępny w obiekcie kontekstu GraphQL. Zgłosi wyjątek, jeśli uwierzytelniony użytkownik nie jest dostępny.
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}
Krok 4 - Utwórz plik Server.js.
Oprogramowanie pośredniczące uwierzytelniania uwierzytelnia wywołujące przy użyciu tokenu internetowego JSON. Adres URL do uwierzytelniania to http://localhost:9000/login.
To jest operacja pocztowa. Użytkownik musi podać swój adres e-mail i hasło, które zostaną zweryfikowane z zaplecza. Jeśli prawidłowy token zostanie wygenerowany metodą jwt.sign, klient będzie musiał przesyłać go w nagłówku dla kolejnych żądań.
Jeśli token jest ważny, req.user zostanie ustawiony z zdekodowanym obiektem JSON do wykorzystania przez późniejsze oprogramowanie pośredniczące do autoryzacji i kontroli dostępu.
Poniższy kod używa dwóch modułów - jsonwebtoken i express-jwt do uwierzytelniania żądań -
Gdy użytkownik kliknie plik greetprzycisku, wysyłane jest żądanie trasy / graphql. Jeśli użytkownik nie jest uwierzytelniony, zostanie poproszony o uwierzytelnienie się.
Użytkownik otrzymuje formularz, który akceptuje identyfikator e-mail i hasło. W naszym przykładzie trasa / login jest odpowiedzialna za uwierzytelnianie użytkownika.
Trasa / login sprawdza, czy w bazie danych znaleziono dopasowanie dla poświadczeń podanych przez użytkownika.
Jeśli poświadczenia są nieprawidłowe, do użytkownika zwracany jest wyjątek HTTP 401.
Jeśli poświadczenia są prawidłowe, serwer generuje token. Ten token jest wysyłany jako część odpowiedzi do użytkownika. Odbywa się to za pomocą funkcji 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});
});
Dla każdego żądania zostanie wywołana funkcja app.use (). To z kolei spowoduje wywołanie oprogramowania pośredniczącego expressJWT. To oprogramowanie pośredniczące dekoduje token sieciowy JSON. Identyfikator użytkownika przechowywany w tokenie zostanie pobrany i zapisany jako użytkownik właściwości w obiekcie żądania.
//decodes the JWT and stores in request object
app.use(expressJwt({
secret: jwtSecret,
credentialsRequired: false
}));
Aby udostępnić właściwość użytkownika w kontekście GraphQL, ta właściwość jest przypisana do context obiekt, jak pokazano poniżej -
//Make req.user available to GraphQL context
app.use('/graphql', graphqlExpress((req) => ({
schema,
context: {user: req.user &&apm; db.students.get(req.user.sub)}
})));
Stwórz server.js w bieżącej ścieżce folderu. Kompletny plik server.js wygląda następująco -
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}`));
Krok 5 - Uruchom aplikację
Wykonaj polecenie npm start w terminalu. Serwer będzie działał na porcie 9000. Tutaj używamy GraphiQL jako klienta do testowania aplikacji.
Otwórz przeglądarkę i wpisz adres URL http://localhost:9000/graphiql. Wpisz następujące zapytanie w edytorze -
{
greetingWithAuth
}
W poniższej odpowiedzi otrzymaliśmy błąd, ponieważ nie jesteśmy uwierzytelnionym użytkownikiem.
{
"data": {
"greetingWithAuth": null
},
"errors": [
{
"message": "Unauthorized",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"greetingWithAuth"
]
}
]
}
W następnej sekcji stwórzmy aplikację kliencką do uwierzytelniania.
Konfigurowanie klienta JQuery
W aplikacji klienckiej dostępny jest przycisk powitania, który wywoła schemat greetingWithAuth. Jeśli klikniesz przycisk bez logowania, wyświetli się komunikat o błędzie, jak poniżej -
Po zalogowaniu się użytkownikiem dostępnym w bazie danych pojawi się następujący ekran -
Mieć dostęp greeting, musimy najpierw uzyskać dostęp do adresu URL http://localhost:9000/login trasa jak poniżej.
Odpowiedź będzie zawierała token wygenerowany z serwera.
$.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')
})
Po pomyślnym zalogowaniu możemy uzyskać dostęp do schematu greetingWithAuth, jak podano poniżej. Powinien istnieć nagłówek Authorizationheader dla wszystkich kolejnych żądań z tokenem okaziciela.
{
url: "http://localhost:9000/graphql",
contentType: "application/json",
headers: {"Authorization": 'bearer '+loginToken}, type:'POST',
data: JSON.stringify({
query:`{greetingWithAuth}`
}
Poniżej znajduje się kod dla 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 = "[email protected]" 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>