<पी> इस लेख में हम नेक्स्ट.जेएस एपीआई रूट्स और अपस्टैश रेडिस का लाभ उठाते हुए एक न्यूनतम लेकिन पूरी तरह कार्यात्मक प्रमाणित रेस्ट एपीआई सेवा का निर्माण करेंगे, जिसे हम अपने डेटा, हमारे उपयोगकर्ता प्रमाणीकरण और हमारे जेडब्ल्यूटी हैंडलिंग दोनों के लिए एक सुपरफास्ट स्टोरेज/कैश सिस्टम के रूप में उपयोग करेंगे। कृपया ध्यान रखें कि इस प्रोजेक्ट में फ्रंट एंड नहीं होगा, लेकिन यह केवल एपीआई को उजागर करेगा जिसे विभिन्न प्रकार के ग्राहकों से पूछताछ की जा सकती है। आवश्यकताएँ
<पी> ट्यूटोरियल का अनुसरण करने के लिए आपको आवश्यकता होगी: - एक अपस्टैश खाता - यहां निःशुल्क खाते के लिए साइन अप करें
- रेडिस का बुनियादी ज्ञान
- नेक्स्ट.जेएस एपीआई रूट्स का बुनियादी ज्ञान
- प्रमाणीकरण और प्राधिकरण वर्कफ़्लो का बुनियादी ज्ञान
- HTTP अनुरोध करने के लिए आपकी पसंद का एक उपकरण
अपस्टैश रेडिस क्या है
<पी> अपस्टैश एक सर्वर रहित इन-मेमोरी क्लाउड डेटाबेस है रेडिस पर आधारित। हम इसका उपयोग उस डेटा को संग्रहीत करने के लिए करेंगे जो हमारे एपीआई द्वारा परोसा जाएगा, हम अपने उपयोगकर्ता आधार और उपयोगकर्ता टोकन को अपस्टैश रेडिस में भी संग्रहीत करेंगे। हम क्या बनाएंगे
<पी> हम एक REST API सेवा को कोड करेंगे जो क्लाइंट एप्लिकेशन को इससे डेटा का अनुरोध करने की अनुमति देगा (इस विशिष्ट मामले में फिल्मों की एक सूची); हम JWT का उपयोग करके अंतिम बिंदुओं को सुरक्षित करेंगे, हम टोकन प्राप्त करने के लिए एक एपीआई लॉगिन सेवा को कोड करेंगे और हम एक ताज़ा टोकन वर्कफ़्लो भी लागू करेंगे। <पी> हम ध्यान केंद्रित नहीं करेंगे ग्राहक विकास पर (चूंकि हम एक 'निर्विवादित' सेवा का निर्माण कर रहे हैं), लेकिन हम अपनी सेवा के विनिर्देश प्रदान करेंगे ताकि कोई भी इसके लिए ग्राहक बना सके। रिपॉजिटरी और डेमो
<पी> अनुसरण करने के लिए आप प्रोजेक्ट रिपॉजिटरी को क्लोन करना चाह सकते हैं <पी> स्रोत GitHub पर <पी> आप निम्न URL पर भी डेमो आज़मा सकते हैं: <पी> https://upstash-dwov9jbiq-popland.vercel.app/api/auth/signin <पी> सेवा से जुड़ने के लिए, उपयोगकर्ता नाम (me@home.org) पास करते हुए एक POST HTTP अनुरोध करें ) और पासवर्ड (पासवर्ड ) जैसे निम्नलिखित उदाहरण में (पोस्टमैन का उपयोग करके): <पी>
रेडिस डेटाबेस की स्थापना
<पी> सबसे पहले, आपको अपस्टैश रेडिस पर साइन-अप करना होगा (परीक्षण उद्देश्यों के लिए एक निःशुल्क योजना काम करेगी), एक बार कंसोल में लॉग-इन करने के बाद आप एक नया डेटाबेस बना सकते हैं: <पी>
<पी> "डेटाबेस बनाएं" पर क्लिक करके आगे बढ़ें, इसे मूवीमैनेजर नाम दें, और इसे ग्लोबल के रूप में सेट करें। अब हम अपस्टैश सीएलआई का उपयोग करके कुछ डमी डेटा जोड़ते हैंपी> <पी>
<पी> हम HMSET कमांड का उपयोग करके कुछ फिल्मों को Redis हैश (वे मूल रूप से ऑब्जेक्ट हैं) के रूप में जोड़ेंगे: hmset movie:’Dr. Strangelove’ director ‘Stanley Kubrick’ year 1964
hmset movie:’2001: A Space Odyssey’ director ‘Stanley Kubrick’ year 1968
hmset movie:’Pulp Fiction’ director ‘Quentin Tarantino’ year 1994
hmset movie:’Django Unchained’ director ‘Quentin Tarantino’ year 2012
<पी> हम एक उपयोगकर्ता भी जोड़ेंगे जो डेटा तक पहुंचने के लिए अधिकृत होगा, उपयोगकर्ता के पास रेडिस हैश भी होगा: hmset user:’me@home.org’ password $2b$10$zctxUVDyy3jzvSp68oKpMOnkyra4R.NzOFVh9aii3Y43X7XtetoyK level 0
<पी> कृपया ध्यान दें :पासवर्ड bcrypt के साथ एन्क्रिप्ट किया गया है (स्पष्ट रूप से यह पासवर्ड है ), आमतौर पर, जिन उपयोगकर्ताओं को किसी वेबसाइट के माध्यम से एपीआई रजिस्टर तक पहुंच की आवश्यकता होती है (या किसी वेबसाइट में उनकी साख प्राप्त होती है), इस उदाहरण में हम एक रजिस्टर एंडपॉइंट प्रदान नहीं करते हैं <पी> यदि सब कुछ सही है और आप डेटा ब्राउज़र पर जाते हैं, तो अपस्टैश सीएलआई में दर्ज प्रत्येक कमांड को आपको एक ठीक उत्तर देना चाहिए। और हैश चुनें, आपके पास आपके द्वारा डाले गए डेटा की सूची होगी <पी>
प्राधिकरण वर्कफ़्लो
<पी> जैसा कि पहले उल्लेख किया गया है, हमारे समापन बिंदु सार्वजनिक रूप से पहुंच योग्य नहीं हैं, इसलिए हमें उपयोगकर्ताओं को प्रमाणित और अधिकृत करने का एक तरीका चाहिए। प्रमाणीकरण के लिए हम एक लॉगिन एंडपॉइंट प्रदान करते हैं; प्राधिकरण के लिए, संरक्षित समापन बिंदु को अनुरोध के साथ भेजे जाने वाले प्राधिकरण हेडर की आवश्यकता होती है। वर्कफ़्लो इस प्रकार विस्तार से काम करता है: - उपयोगकर्ता साइन-इन एंडपॉइंट, उपयोगकर्ता नाम और पासवर्ड पोस्ट करने का अनुरोध करता है
- सर्वर उपयोगकर्ता को प्रमाणित करने का प्रयास करता है, यदि उपयोगकर्ता वैध है तो सर्वर एक JWT (JSON वेब टोकन) और एक रिफ्रेश टोकन बनाता है और वापस भेजता है, रिफ्रेश टोकन हमारे अपस्टैश रेडिस इंस्टेंस पर भी संग्रहीत होता है
- ग्राहक टोकन वापस प्राप्त करता है और उन्हें कहीं संग्रहीत करता है (यह ग्राहक की जिम्मेदारी है कि उन्हें कैसे/कहां संग्रहीत करना है)
- क्लाइंट हेडर में JWT भेजकर एक संरक्षित समापन बिंदु का अनुरोध करता है
- सर्वर JWT प्राप्त करता है, इसे सत्यापित करता है, और यदि यह सत्यापित है तो क्लाइंट द्वारा अनुरोधित डेटा वापस भेजता है
- एक बार जब JWT समाप्त हो जाता है या समाप्त होने के करीब होता है, तो ग्राहक एक विशिष्ट समापन बिंदु पर रिफ्रेश टोकन भेजकर, दोबारा लॉगिन किए बिना नए JWT का अनुरोध कर सकता है।
- सर्वर को रिफ्रेश टोकन प्राप्त होता है, इसे सत्यापित करें, और यदि सत्यापन सकारात्मक है तो एक नया JWT और रिफ्रेश टोकन जारी करें, उन्हें क्लाइंट को वापस भेजें, और नए रिफ्रेश टोकन को फिर से संग्रहीत करें
<पी> जेडब्ल्यूटी और रिफ्रेश टोकन एक ही प्रारूप में हैं, लगभग एक ही जानकारी मिली है लेकिन दो अलग-अलग कुंजियों का उपयोग करें (हम अपने .env में सेट अप करेंगे) फ़ाइल) और दो अलग-अलग समाप्ति तिथियां मिलीं:JWT के लिए एक छोटी समाप्ति (चूंकि यह एक सत्र के दौरान सबसे अधिक उपयोग किया जाने वाला टोकन है, हम इसे जल्द ही समाप्त कर देते हैं, अगर यह बाधित हो जाता है) और ताज़ा टोकन के लिए एक लंबी समाप्ति तिथि है। दोनों की अवधि इस बात पर निर्भर करती है कि आपको कितना सुरक्षित रहना है; आमतौर पर, JWT एक घंटे से भी कम समय में समाप्त हो जाता है, और रिफ्रेश टोकन एक महीने तक चल सकता है। यदि दोनों टोकन समाप्त हो जाते हैं, तो उपयोगकर्ता को फिर से लॉग इन करना होगा। प्रोजेक्ट की स्थापना
<पी> एक बार जब हम अपस्टैश रेडिस डेटाबेस का काम पूरा कर लेते हैं तो हम अपने प्रोजेक्ट को आरंभ करना शुरू कर सकते हैं; सबसे पहले, हम एक नया Next.js प्रोजेक्ट बनाते हैं: npx create-next-app upstash-jwt
<पी> फिर हम नव निर्मित फ़ोल्डर upstash-jwt में प्रवेश करते हैं और हम आवश्यक मॉड्यूल स्थापित करते हैं: npm i bcrypt jsonwebtoken @upstash/redis
<पी> एक .env.local बनाएं अपनी कुंजियाँ संग्रहीत करने और सही डेटा भरने के लिए फ़ाइल SECRET_TOKEN=
SECRET_RTOKEN=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
<पी> SECRET_TOKEN और SECRET_RTOKEN बनाएं जिनका उपयोग JWT उत्पन्न करने के लिए किया जाएगा, कृपया याद रखें कि इन कुंजियों को गुप्त रखा जाना चाहिए और अनुमान लगाना बहुत यादृच्छिक/कठिन होना चाहिए, आप 64 बिट से हेक्स स्ट्रिंग का उपयोग कर सकते हैं। अपने अपस्टैश कंसोल, विवरण टैब, रेस्ट एपीआई अनुभाग से UPSTASH_REDIS_REST_URL और UPSTASH_REDIS_REST_TOKEN प्राप्त करें: <पी>
<पी> अब हम अपने अंतिम बिंदु निर्धारित करना शुरू कर सकते हैं: <पी> पोस्ट/ऑथ/साइनइन यह उपयोगकर्ता को लॉगिन करता है, उसे JSON ऑब्जेक्ट {"email":"email", "password": "password"} के रूप में पारित ईमेल और पासवर्ड की आवश्यकता होती है , यह उपयोगकर्ता की जानकारी एक JWT और एक रिफ्रेश टोकन के साथ एक JSON ऑब्जेक्ट लौटाता है। <पी> /फ़िल्में/ प्राप्त करें फिल्मों की सूची को JSON ऑब्जेक्ट के रूप में लौटाएं, इसके लिए एक वैध JWT की आवश्यकता होती है जिसे इस प्रारूप के साथ हेडर में पारित किया जाता है:प्राधिकरण:बियरर xxx पी> <पी> /movies/$ID प्राप्त करें आईडी $ID के साथ मूवी का विवरण लौटाएँ <पी> पोस्ट/ऑथ/रिफ्रेश एक नया JWT बनाएं और लौटाएं, रिफ्रेश टोकन को refreshToken के रूप में पारित किया जाना चाहिए पैरामीटर. एपीआई मार्गों के लिए कोड
<पी> हम साइन इन एंडपॉइंट से शुरू करते हैं, आइए फ़ाइल pages/api/auth/signin.js बनाएं इस प्रकार: import bcrypt from "bcrypt";
import {
addToList,
generateAccessToken,
generateRefreshToken,
redis,
} from "../../../utils";
export default async (req, res) => {
if (req.method === "GET") {
res.status(405).send("Not Allowed");
} else {
console.log(req.body.user);
try {
const user = await redis.hgetall(`user:${req.body.user}`);
if (user) {
const validPassword = bcrypt.compare(req.body.password, user.password);
if (validPassword) {
const token = generateAccessToken(req.body.user, user.level);
const refreshToken = generateRefreshToken(req.body.user, user.level);
const refresh = await addToList(req.body.user, refreshToken);
const content = {
user: req.body.user,
level: user.level,
};
res.status(200).json({
message: "Logged in",
content: content,
JWT: token,
refresh: refreshToken,
});
} else {
res.status(400).json({ error: "Invalid Password" });
}
} else {
res.status(401).json({ error: "User not found" });
}
} catch (error) {
res.status(500).send("Internal Server Error");
}
}
};
<पी> हमारा साइन-इन एंडपॉइंट केवल दो मापदंडों के साथ POST स्वीकार करेगा:उपयोगकर्ता और पासवर्ड . सबसे पहले, हम जाँचते हैं कि क्या उपयोगकर्ता हमारे Redis डेटाबेस में मौजूद है: const user = await redis.hgetall(`user:${req.body.user}`);
<पी> यदि उपयोगकर्ता मौजूद है तो हम एन्क्रिप्टेड पासवर्ड की तुलना करते हैं: const validPassword = bcrypt.compare(req.body.password, user.password);
<पी> इस बिंदु पर, यदि पासवर्ड मेल खाता है, तो हम मान सकते हैं कि उपयोगकर्ता प्रमाणित है और हम एक JWT और एक ताज़ा टोकन वापस भेज सकते हैं, हम ताज़ा टोकन को अपने Redis उदाहरण में भी संग्रहीत करते हैं। ऐसा करने के लिए हम कुछ फ़ंक्शन का उपयोग करते हैं जो utils.js नामक एक बाहरी फ़ाइल में हैं। पी> <पी> लौटाए गए टोकन को संग्रहीत करना, जरूरत पड़ने पर प्राधिकरण के लिए उनका उपयोग करना और समाप्त होने पर उन्हें ताज़ा करना ग्राहक की ज़िम्मेदारी है <पी> हमारे पास अपना टोकन generateAccessToken उत्पन्न करने के लिए एक फ़ंक्शन है , एक हमारा ताज़ा टोकन generateRefreshToken उत्पन्न करने के लिए , और एक हमारे रेडिस addToList में ताज़ा टोकन को संग्रहीत करने के लिए . यह utils.js इस फ़ाइल का उपयोग अन्य सभी उपयोगिता कार्यों और संदर्भों (जैसे रेडिस कनेक्शन, टोकन सत्यापन और रीफ्रेश इत्यादि) को रखने के लिए भी किया जाएगा: import { Redis } from "@upstash/redis";
import jwt from "jsonwebtoken";
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
export function generateAccessToken(username, email, level) {
return jwt.sign(
{ user: username, email: email, level: level },
process.env.SECRET_TOKEN,
{
expiresIn: "1h",
},
);
}
export function generateRefreshToken(username, email, level) {
return jwt.sign(
{ user: username, email: email, level: level },
process.env.SECRET_RTOKEN,
{
expiresIn: "30d",
},
);
}
export async function addToList(user, refresher) {
try {
await redis.hset("refresh:" + user, { refresh: refresher });
} catch (error) {
console.log(error);
}
}
export async function tokenRefresh(refreshtoken, res) {
var decoded = "";
try {
decoded = jwt.verify(refreshtoken, process.env.SECRET_RTOKEN);
} catch (error) {
return res.status(401).send("Can't refresh. Invalid Token");
}
if (decoded) {
try {
const rtoken = await redis.hget("refresh:" + decoded.user, "refresh");
console.log(rtoken);
if (rtoken !== refreshtoken) {
return res.status(401).send("Can't refresh. Invalid Token");
} else {
const user = await redis.hgetall(`user:${decoded.user}`);
console.log(user);
const token = generateAccessToken(decoded.user, user.level);
const refreshToken = generateRefreshToken(decoded.user, user.level);
const refresh = await addToList(decoded.user, refreshToken);
const content = {
user: decoded.user,
level: user.level,
};
return {
message: "Token Refreshed",
content: content,
JWT: token,
refresh: refreshToken,
};
}
} catch (error) {
console.log(error);
}
}
}
export async function verifyToken(token, res) {
try {
const decoded = jwt.verify(token, process.env.SECRET_TOKEN);
return decoded;
} catch (err) {
return res.status(405).send("Token is invalid");
}
}
<पी> अब, हम (http://localhost:3000/api/auth/signin पर पोस्ट करके हस्ताक्षर प्रक्रिया का परीक्षण करने के लिए एक टूल (जैसे पोस्टमैन) का उपयोग कर सकते हैं। और उपयोगकर्ता नाम (me@home.org) पास करना ) और पासवर्ड (password ), आपको JWT और रिफ्रेश टोकन के साथ उपयोगकर्ता विवरण वाला JSON ऑब्जेक्ट वापस मिलना चाहिए: <पी>
<पी> यदि सब कुछ सही था, तो आपके Redis डेटाबेस में अब आपको नव निर्मित रिफ्रेश टोकन के लिए एक नई हैश प्रविष्टि देखनी चाहिए: <पी>
<पी> इसके बाद, हम टोकन रिफ्रेश रूट refresh.js को कोड करके प्रमाणीकरण प्रक्रिया पूरी कर रहे हैं पी> import { redis, tokenRefresh } from "../../../utils";
export default async (req, res) => {
if (req.method === "GET") {
res.status(405).send("Not Allowed");
} else {
console.log(req.body.refresh);
const refresp = await tokenRefresh(req.body.refresh, res);
res.status(200).json(refresp);
}
};
<पी> यह टोकनरीफ्रेश का उपयोग करता है utils.js से कार्य करें यह यह सत्यापित करने से शुरू होता है कि टोकन वैध है और इसे डीकोड किया जा सकता है, फिर यह रेडिस पर जांच करता है कि क्या उपयोगकर्ता को ताज़ा टोकन मिला है (जिसे हमने पहले addToList के साथ संग्रहीत किया था) ), यदि सब कुछ सही है तो यह एक नया JWT, एक नया ताज़ा टोकन उत्पन्न करता है (और इसे फिर से Redis में संग्रहीत करता है) और सब कुछ क्लाइंट को वापस भेज देता है। <पी> हम अपने टूल का उपयोग करके इस समापन बिंदु का परीक्षण कर सकते हैं, http://localhost:3000/api/auth/refresh पर पोस्ट कर सकते हैं और ताज़ा टोकन को एक पैरामीटर के रूप में पास करना: <पी>
<पी> अब, हमारा काल्पनिक ग्राहक लॉग इन करने और अपने टोकन को रीफ्रेश करने में सक्षम है, आइए देखें कि प्रमाणित अनुरोध करने के लिए टोकन का उपयोग कैसे किया जा सकता है। <पी> एक नया एपीआई रूट बनाएं:api/movies/[[...id]].js इसका उपयोग फिल्मों की सूची प्राप्त करने और फिल्म का विवरण प्राप्त करने के लिए किया जाएगा: import { redis, verifyToken } from "../../../utils";
export default async (req, res) => {
var id;
console.log(req.query);
if (req.query.id) {
id = req.query.id[0];
}
var decoded = "";
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) {
return res.status(403).send("A token is required for authentication");
} else {
decoded = await verifyToken(token, res);
}
if (decoded) {
if (id) {
try {
const result = await redis.hgetall(id);
console.log(result);
return res.status(200).json(result);
} catch (error) {
return res.status(500).send("Internal Server Error");
}
} else {
try {
const result = await redis.scan(0, { match: "movie:*" });
return res.status(200).json(result);
} catch (error) {
return res.status(500).send("Internal Server Error");
}
}
}
};
<पी> verifyToken का उपयोग करना हमारे utils.js से कार्य करें हम अपने एपीआई एंडपॉइंट तक पहुंच को केवल उन उपयोगकर्ताओं तक सीमित कर सकते हैं जो वैध टोकन प्रदान करते हैं। हमने कुछ नमूना प्रश्न पूछे, सबसे पहले फिल्मों की सूची प्राप्त की const result = await redis.scan(0, { match: ‘movie:*’ });
<पी> और दूसरा, यूआरएल अनुरोध में आईडी पैरामीटर के आधार पर एकल फिल्म का विवरण प्राप्त करने के लिए: const result = await redis.hgetall(id);
<पी> दोनों अनुरोध उपयोगकर्ता की स्थिति पर निर्भर हैं जिसे verifyToken के माध्यम से जांचा जाता है लेकिन आप मिश्रण और मिलान कर सकते हैं, उदाहरण के लिए, सूची सार्वजनिक हो सकती है और विवरण सुरक्षित किया जा सकता है। चूंकि हमारे पास उपयोगकर्ता (और टोकन में) में भी एक स्तर संग्रहीत है, इसलिए हमारे पास अधिक प्राधिकरण स्तर हो सकते हैं। आइए फिल्मों की एक सूची प्राप्त करने का प्रयास करें: <पी>
<पी> और एक फिल्म का विवरण: <पी>
ग्राहक परिप्रेक्ष्य
<पी> जैसा कि हमने पहले कहा, हमने सिर्फ सर्वर भाग पर ध्यान केंद्रित किया है, यह एपीआई का मुख्य दायरा है, यह सार होना चाहिए, यह एक वेबसाइट नहीं है। जिस तरह से क्लाइंट डेटा का अनुरोध करता है (प्रोग्रामिंग भाषाएं, लाइब्रेरी इत्यादि) क्लाइंट डेवलपर की पसंद है, हम एंडपॉइंट्स की एक सूची प्रदान करते हैं, हमारे एंडपॉइंट्स क्या उम्मीद कर रहे हैं, और हमारे एंडपॉइंट्स क्लाइंट को क्या लौटाते हैं। डेटा का सभी प्रबंधन, ताज़ा करने में देरी, इत्यादि ग्राहक रणनीति हैं। आगे क्या
<पी> यह संरक्षित एपीआई के वर्कफ़्लो का एक बुनियादी उदाहरण है। यहां से इसे केवल सुधारा जा सकता है:Redis पर डेटा कैसे संग्रहीत किया जाता है, इसका अनुकूलन करना, उपयोगकर्ता डेटा को किसी अन्य Redis इंस्टेंस पर संग्रहीत करके लॉगिन सुरक्षा में सुधार करना, उपयोगकर्ता द्वारा भेजे गए डेटा को सभी से पहले जांचना और मान्य करना, अधिक समापन बिंदु जोड़ना, GraphQL प्रारूप में डेटा लौटाना, अपने API के लिए एक क्लाइंट बनाना, प्रति घंटे अधिकतम संख्या में कॉल तक पहुंच सीमित करना, पहुंच सीमित करने के लिए स्तरों का उपयोग करना... एक्सटेंशन और सुधार अंतहीन हैं!