對於剛接觸GraphQL的開發者來說,身份驗證是極具挑戰性的任務之一。其中涉及許多技術考量,包括選擇易於設置的ORM、如何生成安全的令牌和哈希密碼,甚至包括使用哪個HTTP庫以及如何使用它。
本文重點介紹本地身份驗證。這可能是現代網站處理身份驗證最流行的方式,它通過請求用戶的電子郵件和密碼來實現(與使用Google身份驗證相反)。
此外,本文使用Apollo Server 2、JSON Web Tokens (JWT)和Sequelize ORM來構建一個Node.js身份驗證API。
身份驗證處理
即登錄系統:
- 身份驗證識別或驗證用戶。
- 授權驗證已認證用戶可以訪問的路由(或應用程序的各個部分)。
實現此流程的步驟如下:
- 用戶使用密碼和電子郵件註冊。
- 用戶的憑據存儲在數據庫中。
- 註冊完成後,用戶將被重定向到登錄頁面。
- 經過身份驗證後,用戶將被授予訪問特定資源的權限。
- 用戶的狀態存儲在任何一種瀏覽器存儲介質(例如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中文網其他相關文章!

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

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

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

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

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

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

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

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


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

記事本++7.3.1
好用且免費的程式碼編輯器

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3漢化版
中文版,非常好用

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