搜尋
首頁web前端css教學讓我們使用nodejs和graphql創建自己的身份驗證API

Let's Create Our Own Authentication API with Nodejs and GraphQL

對於剛接觸GraphQL的開發者來說,身份驗證是極具挑戰性的任務之一。其中涉及許多技術考量,包括選擇易於設置的ORM、如何生成安全的令牌和哈希密碼,甚至包括使用哪個HTTP庫以及如何使用它。

本文重點介紹本地身份驗證。這可能是現代網站處理身份驗證最流行的方式,它通過請求用戶的電子郵件密碼來實現(與使用Google身份驗證相反)。

此外,本文使用Apollo Server 2、JSON Web Tokens (JWT)和Sequelize ORM來構建一個Node.js身份驗證API。

身份驗證處理

即登錄系統:

  • 身份驗證識別或驗證用戶。
  • 授權驗證已認證用戶可以訪問的路由(或應用程序的各個部分)。

實現此流程的步驟如下:

  1. 用戶使用密碼和電子郵件註冊。
  2. 用戶的憑據存儲在數據庫中。
  3. 註冊完成後,用戶將被重定向到登錄頁面。
  4. 經過身份驗證後,用戶將被授予訪問特定資源的權限。
  5. 用戶的狀態存儲在任何一種瀏覽器存儲介質(例如localStorage、cookie、會話)或JWT中。

先決條件

在深入實現之前,以下是一些你需要遵循的步驟。

  • Node.js 6或更高版本
  • Yarn(推薦)或NPM
  • GraphQL Playground
  • GraphQL和Node.js的基礎知識
  • …一顆求知的心!

依賴項

這是一個很長的列表,讓我們開始吧:

  • Apollo Server : 一個開源的GraphQL服務器,兼容任何類型的GraphQL客戶端。在這個項目中,我們不會使用Express作為我們的服務器。相反,我們將利用Apollo Server的功能來公開我們的GraphQL API。
  • bcryptjs : 我們希望將用戶密碼哈希到我們的數據庫中。這就是為什麼我們將使用bcrypt。它依賴於Web Crypto API的getRandomValues接口來獲取安全的隨機數。
  • dotenv : 我們將使用dotenv從我們的.env文件中加載環境變量。
  • jsonwebtoken : 用戶登錄後,每個後續請求都將包含JWT,允許用戶訪問使用該令牌允許的路由、服務和資源。 jsonwebtoken將用於生成JWT,用於驗證用戶身份。
  • nodemon : 一個工具,通過在檢測到目錄更改時自動重新啟動Node應用程序來幫助開發基於Node的應用程序。我們不希望每次代碼發生更改時都關閉和啟動服務器。 Nodemon每次都會檢查我們的應用程序中的更改,並自動重新啟動服務器。
  • mysql2 : Node.js的SQL客戶端。我們需要它來連接到我們的SQL服務器,以便我們可以運行遷移。
  • sequelize : Sequelize是一個基於Promise的Node ORM,用於Postgres、MySQL、MariaDB、SQLite和Microsoft SQL Server。我們將使用Sequelize自動生成我們的遷移和模型。
  • sequelize cli : 我們將使用Sequelize CLI來運行Sequelize命令。使用yarn add --global sequelize-cli在終端全局安裝它。

設置目錄結構和開發環境

讓我們創建一個全新的項目。創建一個新文件夾,並在其中創建以下內容:

 <code>yarn init -y</code>

-y標誌表示我們對所有yarn init問題都選擇yes,並使用默認值。

我們還應該在文件夾中放置一個package.json文件,因此讓我們安裝項目依賴項:

 <code>yarn add apollo-server bcryptjs dotenv jsonwebtoken nodemon sequelize sqlite3</code>

接下來,讓我們將Babel添加到我們的開發環境中:

 <code>yarn add babel-cli babel-preset-env babel-preset-stage-0 --dev</code>

現在,讓我們配置Babel。在終端中運行touch .babelrc。這將創建一個並打開一個Babel配置文件,在其中我們將添加以下內容:

 <code>{ "presets": ["env", "stage-0"] }</code>

如果我們的服務器啟動並遷移數據,那就更好了。我們可以通過使用以下內容更新package.json來自動執行此操作:

 <code>"scripts": { "migrate": " sequelize db:migrate", "dev": "nodemon src/server --exec babel-node -e js", "start": "node src/server", "test": "echo \"Error: no test specified\" && exit 1" },</code>

這是我們目前完整的package.json文件:

 <code>{ "name": "graphql-auth", "version": "1.0.0", "main": "index.js", "scripts": {  "migrate": " sequelize db:migrate",  "dev": "nodemon src/server --exec babel-node -e js",  "start": "node src/server",  "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": {  "apollo-server": "^2.17.0",  "bcryptjs": "^2.4.3",  "dotenv": "^8.2.0",  "jsonwebtoken": "^8.5.1",  "nodemon": "^2.0.4",  "sequelize": "^6.3.5",  "sqlite3": "^5.0.0" }, "devDependencies": {  "babel-cli": "^6.26.0",  "babel-preset-env": "^1.7.0",  "babel-preset-stage-0": "^6.24.1" } }</code>

現在我們的開發環境已經設置好了,讓我們轉向數據庫,我們將把東西存儲在那裡。

數據庫設置

我們將使用MySQL作為我們的數據庫,並使用Sequelize ORM進行關係映射。運行sequelize init(假設你之前已經全局安裝了它)。該命令應該創建三個文件夾:/config /models和/migrations。此時,我們的項目目錄結構正在形成。

讓我們配置我們的數據庫。首先,在項目根目錄中創建一個.env文件,並粘貼以下內容:

 <code>NODE_ENV=development DB_HOST=localhost DB_USERNAME= DB_PASSWORD= DB_NAME=</code>

然後轉到我們剛剛創建的/config文件夾,並將其中的config.json文件重命名為config.js。然後,將以下代碼放入其中:

 <code>require('dotenv').config() const dbDetails = { username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, host: process.env.DB_HOST, dialect: 'mysql' } module.exports = { development: dbDetails, production: dbDetails }</code>

在這裡,我們正在讀取我們在.env文件中設置的數據庫詳細信息。 process.env是由Node注入的全局變量,用於表示系統環境的當前狀態。

讓我們使用適當的數據更新我們的數據庫詳細信息。打開SQL數據庫並創建一個名為graphql_auth的表。我使用Laragon作為我的本地服務器,並使用phpmyadmin來管理數據庫表。

無論你使用什麼,我們都需要使用最新信息更新.env文件:

 <code>NODE_ENV=development DB_HOST=localhost DB_USERNAME=graphql_auth DB_PASSWORD= DB_NAME=</code>

讓我們配置Sequelize。在項目的根目錄中創建一個.sequelizerc文件,並粘貼以下內容:

 <code>const path = require('path') module.exports = { config: path.resolve('config', 'config.js') }</code>

現在讓我們將我們的配置集成到模型中。轉到/models文件夾中的index.js,並編輯config變量。

 <code>const config = require(__dirname '/../../config/config.js')[env]</code>

最後,讓我們編寫我們的模型。對於這個項目,我們需要一個User模型。讓我們使用Sequelize自動生成模型。以下是我們在終端中需要運行的內容來設置它:

 <code>sequelize model:generate --name User --attributes username:string,email:string,password:string</code>

讓我們編輯它為我們創建的模型。轉到/models文件夾中的user.js,並粘貼以下內容:

 <code>'use strict'; module.exports = (sequelize, DataTypes) => { const User = sequelize.define('User', {  username: {   type: DataTypes.STRING,  },  email: {   type: DataTypes.STRING,   },  password: {   type: DataTypes.STRING,  } }, {}); return User; };</code>

在這裡,我們為用戶名、電子郵件和密碼創建了屬性和字段。讓我們運行遷移來跟踪我們模式中的更改:

 <code>yarn migrate</code>

現在讓我們編寫模式和解析器。

將模式和解析器與GraphQL服務器集成

在本節中,我們將定義我們的模式,編寫解析器函數,並將它們公開到我們的服務器上。

模式

在src文件夾中,創建一個名為/schema的新文件夾,並在其中創建一個名為schema.js的文件。粘貼以下代碼:

 <code>const { gql } = require('apollo-server') const typeDefs = gql` type User {  id: Int!  username: String  email: String! } type AuthPayload {  token: String!  user: User! } type Query {  user(id: Int!): User  allUsers: [User!]!  me: User } type Mutation {  registerUser(username: String, email: String!, password: String!): AuthPayload!  login (email: String!, password: String!): AuthPayload! } ` module.exports = typeDefs</code>

在這裡,我們從apollo-server導入了graphql-tag。 Apollo Server需要用gql包裝我們的模式。

解析器

在src文件夾中,創建一個名為/resolvers的新文件夾,並在其中創建一個名為resolver.js的文件。粘貼以下代碼:

 <code>const bcrypt = require('bcryptjs') const jsonwebtoken = require('jsonwebtoken') const models = require('../models') require('dotenv').config() const resolvers = {  Query: {   async me(_, args, { user }) {    if(!user) throw new Error('You are not authenticated')    return await models.User.findByPk(user.id)   },   async user(root, { id }, { user }) {    try {     if(!user) throw new Error('You are not authenticated!')     return models.User.findByPk(id)    } catch (error) {     throw new Error(error.message)    }   },   async allUsers(root, args, { user }) {    try {     if (!user) throw new Error('You are not authenticated!')     return models.User.findAll()    } catch (error) {     throw new Error(error.message)    }   }  },  Mutation: {   async registerUser(root, { username, email, password }) {    try {     const user = await models.User.create({      username,      email,      password: await bcrypt.hash(password, 10)     })     const token = jsonwebtoken.sign(      { id: user.id, email: user.email},      process.env.JWT_SECRET,      { expiresIn: '1y' }     )     return {      token, id: user.id, username: user.username, email: user.email, message: "Authentication succesfull"     }    } catch (error) {     throw new Error(error.message)    }   },   async login(_, { email, password }) {    try {     const user = await models.User.findOne({ where: { email }})     if (!user) {      throw new Error('No user with that email')     }     const isValid = await bcrypt.compare(password, user.password)     if (!isValid) {      throw new Error('Incorrect password')     }     // return jwt     const token = jsonwebtoken.sign(      { id: user.id, email: user.email},      process.env.JWT_SECRET,      { expiresIn: '1d'}     )     return {      token, user     }   } catch (error) {    throw new Error(error.message)   }  } }, } module.exports = resolvers</code>

有很多代碼,讓我們看看那裡發生了什麼。

首先,我們導入了我們的模型、bcrypt和jsonwebtoken,然後初始化了我們的環境變量。

接下來是解析器函數。在查詢解析器中,我們有三個函數(me、user和allUsers):

  • me查詢獲取當前登錄用戶的詳細信息。它接受用戶對像作為上下文參數。上下文用於提供對我們數據庫的訪問,該數據庫用於通過查詢中提供的ID加載用戶數據。
  • user查詢根據用戶的ID獲取用戶的詳細信息。它接受id作為上下文參數和一個用戶對象。
  • alluser查詢返回所有用戶的詳細信息。

如果用戶狀態已登錄,則user將是一個對象;如果用戶未登錄,則user將為null。我們將在我們的mutation中創建此用戶。

在mutation解析器中,我們有兩個函數(registerUser和loginUser):

  • registerUser接受用戶的用戶名、電子郵件和密碼,並在我們的數據庫中使用這些字段創建一個新行。需要注意的是,我們使用bcryptjs包使用bcrypt.hash(password, 10)對用戶的密碼進行哈希處理。 jsonwebtoken.sign同步地將給定的有效負載簽名到JSON Web Token字符串(在本例中為用戶ID和電子郵件)。最後,如果成功,registerUser將返回JWT字符串和用戶資料;如果出現錯誤,則返回錯誤消息。
  • login接受電子郵件和密碼,並檢查這些詳細信息是否與提供的詳細信息匹配。首先,我們檢查電子郵件值是否已存在於用戶數據庫中的某個位置。
 <code>models.User.findOne({ where: { email }}) if (!user) { throw new Error('No user with that email') }</code>

然後,我們使用bcrypt的bcrypt.compare方法來檢查密碼是否匹配。

 <code>const isValid = await bcrypt.compare(password, user.password) if (!isValid) { throw new Error('Incorrect password') }</code>

然後,就像我們之前在registerUser中所做的那樣,我們使用jsonwebtoken.sign來生成JWT字符串。 login mutation返回令牌和用戶對象。

現在讓我們將JWT_SECRET添加到我們的.env文件中。

 <code>JWT_SECRET=一个非常长的秘密</code>

伺服器

最後,是服務器!在項目的根文件夾中創建一個server.js,並粘貼以下內容:

 <code>const { ApolloServer } = require('apollo-server') const jwt =  require('jsonwebtoken') const typeDefs = require('./schema/schema') const resolvers = require('./resolvers/resolvers') require('dotenv').config() const { JWT_SECRET, PORT } = process.env const getUser = token => { try {  if (token) {   return jwt.verify(token, JWT_SECRET)  }  return null } catch (error) {  return null } } const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => {  const token = req.get('Authorization') || ''  return { user: getUser(token.replace('Bearer', ''))} }, introspection: true, playground: true }) server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => { console.log(`? Server ready at ${url}`); });</code>

在這裡,我們導入了schema、resolvers和jwt,並初始化了我們的環境變量。首先,我們使用verify驗證JWT令牌。 jwt.verify接受令牌和JWT密鑰作為參數。

接下來,我們使用接受typeDefs和resolvers的ApolloServer實例創建我們的服務器。

我們有一個服務器!讓我們通過在終端中運行yarn dev來啟動它。

測試API

現在讓我們使用GraphQL Playground測試GraphQL API。我們應該能夠通過ID註冊、登錄和查看所有用戶——包括單個用戶。

我們將首先打開GraphQL Playground應用程序,或者只需在瀏覽器中打開localhost://4000即可訪問它。

註冊用戶的Mutation

 <code>mutation { registerUser(username: "Wizzy", email: "[email protected]", password: "wizzyekpot" ){  token } }</code>

我們應該得到類似這樣的結果:

 <code>{ "data": {  "registerUser": {   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzAwLCJleHAiOjE2MzA3OTc5MDB9.gmeynGR9Zwng8cIJR75Qrob9bovnRQT242n6vfBt5PY"  } } }</code>

登錄的Mutation

現在讓我們使用我們剛剛創建的用戶詳細信息登錄:

 <code>mutation { login(email:"[email protected]" password:"wizzyekpot"){  token } }</code>

我們應該得到類似這樣的結果:

 <code>{ "data": {  "login": {   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc"  } } }</code>

太棒了!

單個用戶的Query

為了查詢單個用戶,我們需要將用戶令牌作為授權標頭傳遞。轉到HTTP標頭選項卡。

…並將此內容粘貼進去:

 <code>{ "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" }</code>

這是查詢:

 <code>query myself{ me {  id  email  username } }</code>

我們應該得到類似這樣的結果:

 <code>{ "data": {  "me": {   "id": 15,   "email": "[email protected]",   "username": "Wizzy"  } } }</code>

太棒了!現在讓我們按ID獲取用戶:

 <code>query singleUser{ user(id:15){  id  email  username } }</code>

這是獲取所有用戶的查詢:

 <code>{ allUsers{  id  username  email } }</code>

總結

在構建需要身份驗證的網站時,身份驗證是最困難的任務之一。 GraphQL使我們能夠僅使用一個端點構建完整的身份驗證API。 Sequelize ORM使創建與SQL數據庫的關係變得如此簡單,我們幾乎不必擔心我們的模型。同樣值得注意的是,我們不需要HTTP服務器庫(如Express),而是使用Apollo GraphQL作為中間件。 Apollo Server 2現在使我們能夠創建我們自己的庫無關的GraphQL服務器!

請查看GitHub上的本教程的源代碼。

以上是讓我們使用nodejs和graphql創建自己的身份驗證API的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
使用頁面CMS進行靜態站點內容管理使用頁面CMS進行靜態站點內容管理May 13, 2025 am 09:24 AM

我知道,我知道:有大量的內容管理系統選項可用,而我進行了幾個測試,但實際上沒有一個是一個,y&#039;知道嗎?怪異的定價模型,艱難的自定義,有些甚至最終成為整個&

鏈接HTML中CSS文件的最終指南鏈接HTML中CSS文件的最終指南May 13, 2025 am 12:02 AM

鏈接CSS文件到HTML可以通過在HTML的部分使用元素實現。 1)使用標籤鏈接本地CSS文件。 2)多個CSS文件可通過添加多個標籤實現。 3)外部CSS文件使用絕對URL鏈接,如。 4)確保正確使用文件路徑和CSS文件加載順序,優化性能可使用CSS預處理器合併文件。

CSS Flexbox與網格:全面評論CSS Flexbox與網格:全面評論May 12, 2025 am 12:01 AM

選擇Flexbox還是Grid取決於佈局需求:1)Flexbox適用於一維佈局,如導航欄;2)Grid適合二維佈局,如雜誌式佈局。兩者在項目中可結合使用,提升佈局效果。

如何包括CSS文件:方法和最佳實踐如何包括CSS文件:方法和最佳實踐May 11, 2025 am 12:02 AM

包含CSS文件的最佳方法是使用標籤在HTML的部分引入外部CSS文件。 1.使用標籤引入外部CSS文件,如。 2.對於小型調整,可以使用內聯CSS,但應謹慎使用。 3.大型項目可使用CSS預處理器如Sass或Less,通過@import導入其他CSS文件。 4.為了性能,應合併CSS文件並使用CDN,同時使用工具如CSSNano進行壓縮。

Flexbox vs Grid:我應該學習兩者嗎?Flexbox vs Grid:我應該學習兩者嗎?May 10, 2025 am 12:01 AM

是的,youshouldlearnbothflexboxandgrid.1)flexboxisidealforone-demensional,flexiblelayoutslikenavigationmenus.2)gridexcelstcelsintwo-dimensional,confffferDesignssignssuchasmagagazineLayouts.3)blosebothenHancesSunHanceSlineHancesLayOutflexibilitibilitibilitibilitibilityAnderibilitibilityAndresponScormentilial anderingStruction

軌道力學(或我如何優化CSS KeyFrames動畫)軌道力學(或我如何優化CSS KeyFrames動畫)May 09, 2025 am 09:57 AM

重構自己的代碼看起來是什麼樣的?約翰·瑞亞(John Rhea)挑選了他寫的一個舊的CSS動畫,並介紹了優化它的思維過程。

CSS動畫:很難創建它們嗎?CSS動畫:很難創建它們嗎?May 09, 2025 am 12:03 AM

CSSanimationsarenotinherentlyhardbutrequirepracticeandunderstandingofCSSpropertiesandtimingfunctions.1)Startwithsimpleanimationslikescalingabuttononhoverusingkeyframes.2)Useeasingfunctionslikecubic-bezierfornaturaleffects,suchasabounceanimation.3)For

@KeyFrames CSS:最常用的技巧@KeyFrames CSS:最常用的技巧May 08, 2025 am 12:13 AM

@keyframesispopularduetoitsversatoryand and powerincreatingsmoothcsssanimations.keytricksinclude:1)definingsmoothtransitionsbetnestates,2)使用AnimatingMultatingMultationMultationProperPertiessimultane,3)使用使用4)使用BombingeNtibalibility,4)使用CombanningWiThjavoFofofofoftofofo

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。