เจดับบลิวทีคืออะไร? บทความนี้จะพาคุณมาทำความเข้าใจกับ JWT, แนะนำการใช้งาน JWT ใน node และข้อดีและข้อเสียของ JWT หวังว่าคงจะเป็นประโยชน์กับทุกคนนะครับ!
JWT เป็นตัวย่อของ JSON Web Token ซึ่งเป็นโซลูชันการตรวจสอบสิทธิ์ในสภาพแวดล้อมแอปพลิเคชันเครือข่าย ในกลไกการตรวจสอบสิทธิ์แบบดั้งเดิม ไม่มีอะไรมากไปกว่าขั้นตอนต่อไปนี้:
1. ผู้ใช้ส่งรหัสผ่านบัญชีไปยังเซิร์ฟเวอร์ 2. หลังจากที่เซิร์ฟเวอร์ตรวจสอบรหัสผ่านบัญชีแล้ว เซิร์ฟเวอร์จะบันทึกข้อมูลที่เกี่ยวข้องกับผู้ใช้ บทบาทของผู้ใช้ หรือเวลาหมดอายุ ฯลฯ ในเซสชันปัจจุบัน 3. เซิร์ฟเวอร์ให้ session_id แก่ผู้ใช้และเขียนลงในคุกกี้ของผู้ใช้หรือไคลเอนต์บันทึกไว้ในเครื่อง 4. ทุกครั้งที่ผู้ใช้ร้องขอบริการ เขาหรือเธอจำเป็นต้องนำ session_id นี้มา อาจผ่านทางคุกกี้หรือวิธีการอื่น 5. หลังจากที่เซิร์ฟเวอร์ได้รับเซิร์ฟเวอร์จะกลับไปที่ฐานข้อมูลเพื่อสอบถาม session_id ปัจจุบันและตรวจสอบว่าผู้ใช้มีสิทธิ์หรือไม่
ข้อดีของโมเดลนี้คือเซิร์ฟเวอร์สามารถยกเลิกการอนุญาตของผู้ใช้ได้ตลอดเวลา และสามารถไปที่ฐานข้อมูลเพื่อแก้ไขหรือลบข้อมูลเซสชันของผู้ใช้ปัจจุบันได้ แต่ก็มีข้อเสียเช่นกัน กล่าวคือ หากเป็นคลัสเตอร์เซิร์ฟเวอร์ เครื่องทั้งหมดจำเป็นต้องแชร์ข้อมูลเซสชันเพื่อให้แน่ใจว่าแต่ละเซิร์ฟเวอร์สามารถรับข้อมูลที่จัดเก็บข้อมูลเซสชันเดียวกันได้ แม้ว่าปัญหาเหล่านี้จะสามารถแก้ไขได้ แต่ปริมาณงานก็มีมาก
ข้อดีของโซลูชัน JWT คือข้อมูลนี้จะไม่ได้รับการบันทึกบนไคลเอ็นต์ ทุกครั้งที่ยอมรับคำขอ จะต้องได้รับการตรวจสอบเท่านั้น
เรามาพูดคุยสั้นๆ เกี่ยวกับหลักการของ JWT กันดีกว่า ที่จริงแล้ว เมื่อไคลเอนต์ส่งคำขอตรวจสอบสิทธิ์ เซิร์ฟเวอร์จะสร้างออบเจ็กต์ JSON หลังจากตรวจสอบสิทธิ์ผู้ใช้ ซึ่งอาจรวมถึงข้อมูลเช่น "คุณเป็นใคร คุณทำอะไร เป็นต้น ., เวลาหมดอายุ " สิ่งสำคัญคือต้องมีเวลาหมดอายุ รูปแบบทั่วไปคือ:
- ชื่อผู้ใช้: "thief string เอ้อ", บทบาท: "World Code Farmer" endTime: "20 พฤษภาคม 2022" -
แต่จะไม่มีการส่งต่อให้คุณในลักษณะผิวเผิน มันจะถูกลงนามและส่งผ่านอัลกอริธึมลายเซ็นแบบย้อนกลับตามอัลกอริธึมลายเซ็นที่ระบุและข้อมูลบางอย่างเกี่ยวกับเพย์โหลดที่คุณส่งมา ฉันจะใช้รูปภาพเพื่อแสดงรูปแบบทั่วไป : :
ดังที่เห็นจากรูปภาพข้อมูลที่ส่งคืนจะแบ่งออกเป็นสามส่วนโดยประมาณ ด้านซ้ายคือผลลัพธ์หลังจากการลงนามซึ่งเป็นผลลัพธ์ที่ส่งคืนไปยังไคลเอนต์ คั่นด้วย "จุด" ตามลำดับ มีการติดต่อกันแบบหนึ่งต่อหนึ่งระหว่างสีแดง สีม่วง และสีฟ้า:
ส่วนสีแดงแรกคือส่วนหัว ส่วนหัวจะระบุวิธีการเป็นหลัก ( ค่าเริ่มต้น HS256 ) คือ HMAC ที่มี SHA-256 เป็นการแชร์คีย์เดียวระหว่างทั้งสองฝ่าย พิมพ์ The ตัวระบุฟิลด์เป็นประเภท JWT;
ส่วนสีม่วงที่สอง เพย์โหลด คือออบเจ็กต์ JSON ซึ่งเป็นข้อมูลจริงที่จะส่ง มีฟิลด์ทางการ 7 ฟิลด์ที่สามารถใช้ได้:
iss (ผู้ออก): ผู้ออก
exp (เวลาหมดอายุ): เวลาหมดอายุ
ย่อย (หัวเรื่อง): หัวเรื่อง
aud (ผู้ชม): ผู้ชม
nbf (ไม่ก่อน): เวลาที่มีผล
iat (ออกเมื่อ): เวลาที่ออก
jti (JWT ID): หมายเลข
นอกจากฟิลด์เหล่านี้แล้ว คุณยังสามารถสร้างฟิลด์แบบกำหนดเองบางฟิลด์ได้ เนื่องจาก JWT ไม่ได้ถูกเข้ารหัสตามค่าเริ่มต้น โปรดใช้ความระมัดระวังอย่าใช้ข้อมูลที่ละเอียดอ่อนเมื่อใช้งาน
ส่วนที่สามคือลาย Signature
ส่วนนี้เป็นคีย์ลับที่คุณระบุและมีอยู่บนเซิร์ฟเวอร์เท่านั้น จากนั้นใช้อัลกอริทึมที่ระบุในส่วนหัวเพื่อลงชื่อผ่านวิธีลายเซ็นต่อไปนี้
มาสัมผัสประสบการณ์การใช้งานเฉพาะด้านล่างนี้:
ขั้นตอนที่ 1: เราจำเป็นต้องสร้างโครงการ nodejs เริ่มต้นโครงการผ่าน npm init -y
จากนั้นเราจำเป็นต้องติดตั้งการพึ่งพารวมถึง express
, jsonwebtoken
และ nodemon
:
$ npm ฉันแสดง jsonwebtoken nodemon
จากนั้นเพิ่มคำสั่ง nodemon app.js
ในช่อง scripts
ใน package.json
:
"สคริปต์": { "start": "nodemon app.js" -
ขั้นตอนที่ 2: เริ่มต้นแอปพลิเคชันโหนดและสร้างไฟล์ app.js ในไดเร็กทอรีราก
// app.js const express = ต้องการ ("ด่วน"); แอป const = ด่วน (); app.use(express.json()); app.listen(3000, () => { console.log(3000 + "กำลังฟัง..."); // ฟังพอร์ต 3000});
ขั้นตอนที่ 3: แนะนำการพึ่งพา jsonwebtoken
และสร้างคีย์ส่วนตัวของอินเทอร์เฟซและเซิร์ฟเวอร์
// app.js - const jwt = ต้องการ("jsonwebtoken"); const jwtKey = "~!@#$%^&*()+,"; -
jwtKey
ในที่นี้คือคีย์ส่วนตัวที่เรากำหนดเองซึ่งบันทึกไว้ในเซิร์ฟเวอร์เท่านั้น หลังจากนั้น เราเริ่มเขียนอินเทอร์เฟซ /login สำหรับการเข้าสู่ระบบ และสร้างฐานข้อมูลจำลองในเครื่องสำหรับการตรวจสอบ และดำเนินการผ่านวิธี jwt.sign
ยืนยันลายเซ็น:
// app.js ฐานข้อมูล const = { ชื่อผู้ใช้: "ชื่อผู้ใช้", รหัสผ่าน: "รหัสผ่าน", - app.post("/login", (req, res) => { const { ชื่อผู้ใช้ รหัสผ่าน } = req.body; ถ้า (ชื่อผู้ใช้ === ฐานข้อมูลชื่อผู้ใช้ && รหัสผ่าน === ฐานข้อมูลรหัสผ่าน) { jwt.ลงชื่อ( - ชื่อผู้ใช้, - เจดับบลิวทีคีย์, - หมดอายุใน: "30S", - (_, โทเค็น) => { res.json({ ชื่อผู้ใช้, ข้อความ: "เข้าสู่ระบบสำเร็จ", โทเค็น, - - - - -
ในโค้ดข้างต้น เราได้สร้างตัวแปร database
เพื่อจำลองการสร้างบัญชีในเครื่องและฐานข้อมูลรหัสผ่านเพื่อตรวจสอบการเข้าสู่ระบบ จากนั้นเราได้สร้างอินเทอร์ post
สต์ /login
ขึ้นมา หลังจากตรวจสอบว่าบัญชีและรหัสผ่านตรงกันอย่างสมบูรณ์แล้ว เราก็นำเข้าข้อมูลดังกล่าวผ่าน jsonwebtoken
แพ็คเกจ ใช้วิธี sign
ภายใต้วัตถุ jwt
เพื่อเซ็นชื่อ
เครื่องหมายฟังก์ชันการส่งออก( เพย์โหลด: string | .buffer | . secretOrPrivateKey: ความลับ, ตัวเลือก?: SignOptions, ): สตริง; เครื่องหมายฟังก์ชันการส่งออก( เพย์โหลด: string | .buffer | . secretOrPrivateKey: ความลับ, โทรกลับ: SignCallback, ): เป็นโมฆะ; เครื่องหมายฟังก์ชันการส่งออก( เพย์โหลด: string | .buffer | . secretOrPrivateKey: ความลับ, ตัวเลือก: SignOptions, โทรกลับ: SignCallback, ): เป็นโมฆะ;
วิธีการโอเวอร์โหลดฟังก์ชันถูกใช้ที่นี่เพื่อใช้อินเทอร์เฟซ เราจะใช้ลายเซ็นอินเทอร์เฟซสุดท้ายที่นี่ พารามิเตอร์แรกอาจเป็นประเภทออบเจ็กต์ที่กำหนดเอง ประเภท Buffer
หรือประเภท string
โดยตรง ซอร์สโค้ดของเราใช้ประเภท object
และปรับแต่งบางฟิลด์ เนื่องจาก JWT จะลงนามข้อมูลเหล่านี้เมื่อลงนาม อย่างไรก็ตาม เป็นที่น่าสังเกตว่าคุณควรพยายามอย่าใช้ข้อมูลที่ละเอียดอ่อนที่นี่ เนื่องจาก JWT ไม่ได้เข้ารหัสตามค่าเริ่มต้น แกนหลักคือ ลายเซ็น ซึ่งทำให้แน่ใจได้ว่าข้อมูล ไม่ได้ถูกดัดแปลง และกระบวนการตรวจสอบลายเซ็นเรียกว่า การตรวจสอบ
แน่นอน คุณยังสามารถเข้ารหัสโทเค็นดั้งเดิมแล้วส่งได้
พารามิเตอร์ตัวที่สอง: เป็นคีย์ลับที่เราบันทึกไว้บนเซิร์ฟเวอร์สำหรับการลงนาม โดยปกติแล้วในโหมดไคลเอ็นต์-เซิร์ฟเวอร์ JWS จะใช้อัลกอริทึม HS256 ที่ JWA มอบให้บวกกับคีย์ อย่างไรก็ตาม ในสถานการณ์แบบกระจาย อาจจำเป็นต้องตรวจสอบบริการหลายอย่างเพื่อยืนยัน JWT หากรหัสถูกบันทึกไว้ในแต่ละบริการ การรักษาความปลอดภัยจะลดลงอย่างมาก คุณต้องรู้ว่าเมื่อรหัสรั่วไหล ใครๆ ก็สามารถปลอมแปลง JWT ได้ตามต้องการ
พารามิเตอร์ที่สาม: คือตัวเลือกลายเซ็น SignOptions
ลายเซ็นของอินเทอร์เฟซ:
ส่งออกอินเทอร์เฟซ SignOptions { อัลกอริทึม?: อัลกอริทึม | ไม่ได้กำหนด; keyid?: สตริง | . ไม่ได้กำหนด; หมดอายุใน?: สตริง | . หมายเลข | ไม่ได้กำหนด; /** แสดงเป็นวินาทีหรือสตริงที่อธิบายช่วงเวลา [zeit/ms](https://github.com/zeit/ms.js) เช่น 60, "2 วัน", "10h", "7d" */ notBefore?: สตริง | . หมายเลข | . ผู้ชม?: string | .string[] | . เรื่อง?: สตริง | . ไม่ได้กำหนด; ผู้ออก?: string | . ไม่ได้กำหนด; jwtid?: สตริง | . กลายพันธุ์Payload?: บูลีน | . ไม่ได้กำหนด; noTimestamp?: บูลีน | . ไม่ได้กำหนด; ส่วนหัว?: JwtHeader | . ไม่ได้กำหนด; การเข้ารหัส?: สตริง | . ไม่ได้กำหนด; -
ที่นี่เราใช้ฟิลด์ expiresIn
ในเพื่อระบุระยะเวลาการใช้งาน โปรดดูเอกสารนี้สำหรับวิธีการใช้งาน
พารามิเตอร์ที่สี่คือการเรียกกลับ พารามิเตอร์ที่สองของการโทรกลับคือ token
ที่เราสร้างผ่านลายเซ็น ในที่สุด token
นี้จะถูกส่งกลับไปยังส่วนหน้าเพื่อให้สามารถจัดเก็บไว้ในส่วนหน้าและนำไปที่เซิร์ฟเวอร์เพื่อตรวจสอบ ในทุกคำขอ
ต่อไป มาตรวจสอบอินเทอร์เฟซนี้: ฉันติดตั้งปลั๊กอิน REST Client ใน vscode แล้วสร้างไฟล์ request.http
ในไดเร็กทอรีราก และเขียนข้อมูลที่ร้องขอในไฟล์:
โพสต์ http://localhost:3000/login ประเภทเนื้อหา: application/json - "ชื่อผู้ใช้": "ชื่อผู้ใช้", "รหัสผ่าน": "รหัสผ่าน" -
จากนั้นดำเนินการคำสั่ง npm run start
บนบรรทัดคำสั่งเพื่อเริ่มบริการ จากนั้นคลิกปุ่ม Send Request
เหนือไฟล์ requset.http
เพื่อส่งคำขอ:
หลังจากที่คำขอสำเร็จ คุณจะเห็นข้อความตอบกลับดังนี้:
ฟิลด์ token
คือ token
ที่สร้างโดย JWT ของเรา
มาตรวจสอบว่า token
นี้ถูกต้องหรือไม่ เรากำลังเขียนอินเทอร์เฟซหลังจากเข้าสู่ระบบ:
app.get("/afterlogin", (req, res) => { const { ส่วนหัว } = คำขอ; โทเค็น const = ส่วนหัว ["การอนุญาต"].split(" ")[1]; //ใส่โทเค็นในช่องการอนุญาตของส่วนหัว jwt.verify(token, jwtKey, (err, payload) => { ถ้า (ผิดพลาด) ส่งคืน res.sendStatus (403); res.json ({ ข้อความ: "การรับรองความถูกต้องสำเร็จ", เพย์โหลด }); - -
ในโค้ดนี้ token
ที่สร้างก่อนหน้านี้ผ่าน JWT จะได้รับโดยการรับ token
ในช่อง authorization
ในส่วนหัวของคำขอ จากนั้นตรวจสอบว่า token
นั้นถูกต้องหรือไม่โดยการเรียกวิธีการยืนยัน jwt.verify
วิธีนี้มีพารามิเตอร์สามตัว:
// มีลายเซ็นอินเทอร์เฟซสี่แบบ คุณสามารถตรวจสอบเอกสารได้ด้วยตัวเอง ส่งออกฟังก์ชันตรวจสอบ( โทเค็น: สตริง //โทเค็นที่ต้องตรวจสอบ secretOrPublicKey: ความลับ | . GetPublicKeyOrSecret, // รหัสลายเซ็นที่กำหนดไว้ในการเรียกกลับของเซิร์ฟเวอร์: VerifyCallback<JwtPayload | . // โทรกลับเพื่อรับผลข้อมูลการตรวจสอบ): เป็นโมฆะ;
ต่อไป เราจะคัดลอก token
เราเพิ่งตอบกลับไปลงในส่วนหัวของคำขอ:
- รับ http://localhost:3000/afterlogin การอนุญาต: ผู้ถือ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQ N5 8Hk_XEP5y9GM9A8jBbY
การรับรองความถูกต้องของ Bearer ก่อนหน้านี้เป็นวิธีการตรวจสอบความถูกต้องมาตรฐานในโปรโตคอล http
คลิก Send Request
ด้วย และเมื่อคุณเห็นการตอบกลับในภาพด้านล่าง แสดงว่าการตอบกลับสำเร็จ:
ที่จริงแล้ว ข้างต้นคือการใช้งาน JWT แบบง่ายๆ ต่อไป เรามาพูดถึงข้อดีและข้อเสียของ JWT กันดีกว่า
พื้นที่เก็บข้อมูลที่ JWT ครอบครองนั้นไม่เล็กเลย หากเราต้องการเซ็นชื่อข้อมูลมากเกินไป โทเค็นก็มีแนวโน้มที่จะเกินขีดจำกัดความยาวของคุกกี้ ตัวอย่างเช่น เปรียบเทียบสองรูปภาพนี้:
แน่นอนว่าเมื่อปริมาณข้อมูลในเพย์โหลดเพิ่มขึ้น ความยาวของโทเค็นก็จะเพิ่มขึ้นเช่นกัน
ที่จริงแล้ว ความปลอดภัย หาก token
ใช้พื้นที่มากเกินไป พื้นที่เก็บข้อมูลสูงสุด Cookie
จะอยู่ที่ 4kb เท่านั้น ส่วนหน้าสามารถจัดเก็บไว้ในที่จัดเก็บในตัวเครื่อง เช่น localStorage
ได้ แต่จะทำให้เกิดปัญหาได้ ในคุกกี้ความปลอดภัยจะลดลงอย่างมาก จะมีความเสี่ยงในการรับมันผ่านสคริปต์ js ซึ่งหมายความว่าแฮ็กเกอร์คนใดก็ตามสามารถทำอะไรกับมันได้
ความตรงต่อเวลาที่ไม่ยืดหยุ่น อันที่จริง แง่มุมหนึ่งของ JWT ก็คือไม่จำเป็นต้องจัดเก็บ token
อย่างต่อเนื่อง แต่ token
จะได้รับการตรวจสอบอย่างมีประสิทธิภาพโดยใช้การตรวจสอบเซิร์ฟเวอร์ เวลาหมดอายุมีการเปลี่ยนแปลง token
จะถูกแก้ไข เนื่องจากไม่มีวิธีจัดเก็บและเปลี่ยนวันหมดอายุด้วยตนเอง จึงเป็นเรื่องยากที่จะลบ token
ทันที หากผู้ใช้เข้าสู่ระบบสองครั้งและสร้าง token
สองรายการ จากนั้นจึงเข้าสู่ระบบ โดยหลักการแล้ว สอง token
จะถูกสร้างขึ้น
ข้างต้นส่วนใหญ่พูดถึงบางประเด็น:
หลักการของ JWT คือการใช้คีย์ส่วนตัวของเซิร์ฟเวอร์เพื่อสื่อสารกับ token
ที่สร้างโดยลายเซ็น JSON เป็นหลัก
นอกจากนี้ยังแนะนำองค์ประกอบของข้อมูลภายในของ JWT ซึ่ง Header ใช้เพื่อระบุอัลกอริธึมและประเภทของลายเซ็น เพย์โหลดในการส่งข้อมูล JSON และ Signature เพื่อใช้อัลกอริธึมลายเซ็นกับข้อมูลและป้องกันการปลอมแปลง
มีการแนะนำโดยละเอียดเกี่ยวกับวิธีใช้ JWT ผ่าน nodejs ดำเนินการลงนามข้อมูลด้วยวิธี sign
และดำเนินการตรวจสอบลายเซ็นด้วยวิธี verify
มีการแนะนำข้อเสียบางประการของ JWT:
ประการแรกคือพื้นที่เก็บข้อมูลจะเพิ่มขึ้นตามจำนวนข้อมูลลายเซ็นที่เพิ่มขึ้น
จากนั้นจะมีการรักษาความปลอดภัย หากพื้นที่เก็บข้อมูลมีขนาดใหญ่เกินไปก็จะไม่ถูกจัดเก็บไว้ใน Cookie
ที่มีระดับความปลอดภัยค่อนข้างสูงทำให้ได้รับสคริปต์ตามต้องการ
จากนั้นก็มีความทันเวลาซึ่งไม่สามารถควบคุมความทันเวลาของ token
ได้อย่างยืดหยุ่น
นี่คือ ซอร์สโค้ดสาธิต ของ nodejs ด้านบนสำหรับการอ้างอิง