วันนี้เราจะมาดูกันว่าการผสานรวมนั้นง่ายเพียงใด โทเค็นเว็บ JSON (JWT) การรับรองความถูกต้องในไฟล์ เชิงมุม 6 (หรือใหม่กว่า) แอปพลิเคชันหน้าเดียว (SPA) มาเริ่มต้นด้วยพื้นหลังเล็กน้อย
คำตอบที่ง่ายและรัดกุมที่สุดคือสะดวกกะทัดรัดและปลอดภัย มาดูรายละเอียดการอ้างสิทธิ์เหล่านั้นกัน:
สิ่งที่กล่าวมาทั้งหมดนี้คือคุณมีวิธีที่ปลอดภัยและมีประสิทธิภาพในการตรวจสอบสิทธิ์ผู้ใช้จากนั้นตรวจสอบการเรียกไปยังปลายทาง API ของคุณโดยไม่ต้องแยกวิเคราะห์โครงสร้างข้อมูลใด ๆ หรือใช้การเข้ารหัสของคุณเอง
ดังนั้นด้วยความเป็นมาเล็กน้อยตอนนี้เราสามารถเจาะลึกถึงวิธีการทำงานในแอปพลิเคชันจริง สำหรับตัวอย่างนี้ฉันจะสมมติว่าเรามีเซิร์ฟเวอร์ Node.js ที่โฮสต์ API ของเราและเราก็คือ การพัฒนาสปา รายการทั้งหมดโดยใช้ เชิงมุม 6. มาทำงานกับโครงสร้าง API นี้กัน:
/auth
→ POST
(โพสต์ชื่อผู้ใช้และรหัสผ่านเพื่อตรวจสอบสิทธิ์และรับ JWT กลับ) /todos
→ GET
(ส่งคืนรายการสิ่งที่ต้องทำรายการสำหรับผู้ใช้) /todos/{id}
→ GET
(ส่งคืนรายการสิ่งที่ต้องทำเฉพาะ) /users
→ GET
(ส่งคืนรายชื่อผู้ใช้) เราจะดำเนินการสร้างแอปพลิเคชันง่ายๆนี้ในไม่ช้า แต่สำหรับตอนนี้เราจะมุ่งเน้นไปที่การโต้ตอบในทางทฤษฎี เรามีหน้าเข้าสู่ระบบที่เรียบง่ายซึ่งผู้ใช้สามารถป้อนชื่อผู้ใช้และรหัสผ่านได้ เมื่อส่งแบบฟอร์มแล้วระบบจะส่งข้อมูลนั้นไปยัง /auth
จุดสิ้นสุด จากนั้นเซิร์ฟเวอร์โหนดสามารถตรวจสอบสิทธิ์ผู้ใช้ในรูปแบบใดก็ได้ที่เหมาะสม (การค้นหาฐานข้อมูลการสอบถามบริการเว็บอื่น ฯลฯ ) แต่ในที่สุดปลายทางจะต้องส่งคืน JWT
JWT สำหรับตัวอย่างนี้จะมีบางส่วน การอ้างสิทธิ์ที่สงวนไว้ และบางส่วน การอ้างสิทธิ์ส่วนตัว . การอ้างสิทธิ์ที่สงวนไว้เป็นเพียงคู่คีย์ - ค่าที่แนะนำโดย JWT ที่ใช้กันทั่วไปในการตรวจสอบสิทธิ์ในขณะที่การอ้างสิทธิ์ส่วนตัวเป็นคู่คีย์ - ค่าที่ใช้ได้กับแอปของเราเท่านั้น:
การอ้างสิทธิ์ที่สงวนไว้
iss
: ผู้ออกโทเค็นนี้ โดยทั่วไปแล้ว FQDN ของเซิร์ฟเวอร์ แต่อาจเป็นอะไรก็ได้ตราบเท่าที่แอปพลิเคชันไคลเอนต์รู้ว่าจะคาดหวังexp
: วันที่และเวลาหมดอายุของโทเค็นนี้ นี่คือหน่วยวินาทีนับตั้งแต่เที่ยงคืน 01 มกราคม 1970 GMT (เวลา Unix) nbf
: ใช้ไม่ได้ก่อนประทับเวลา ไม่ได้ใช้บ่อย แต่ให้ขอบเขตล่างสำหรับหน้าต่างความถูกต้อง รูปแบบเดียวกับ exp
. การอ้างสิทธิ์ส่วนตัว
uid
: ID ผู้ใช้ของผู้ใช้ที่ล็อกอินrole
: บทบาทที่กำหนดให้กับผู้ใช้ที่ล็อกอินข้อมูลของเราจะเข้ารหัส base64 และลงนามโดยใช้ HMAC ด้วยคีย์ที่แชร์ todo-app-super-shared-secret
ด้านล่างนี้เป็นตัวอย่างลักษณะของ JWT:
s corp และ c corp ความแตกต่าง
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b2RvYXBpIiwibmJmIjoxNDk4MTE3NjQyLCJleHAiOjE0OTgxMjEyNDIsInVpZCI6MSwicm9sZSI6ImFkbWluIn0.ZDz_1vcIlnZz64nSM28yA1s-4c_iw3Z2ZtP-SgcYRPQ
นี้ สตริงคือทั้งหมดที่เราต้องตรวจสอบให้แน่ใจว่าเรามีการเข้าสู่ระบบที่ถูกต้องเพื่อให้ทราบว่าผู้ใช้รายใดเชื่อมต่ออยู่และรู้ว่าผู้ใช้มีบทบาทอะไร
ไลบรารีและแอปพลิเคชันส่วนใหญ่ดำเนินการจัดเก็บ JWT นี้ใน localStorage
หรือ sessionStorage
เพื่อให้ค้นคืนได้ง่าย แต่นี่เป็นเพียงแนวทางปฏิบัติทั่วไป สิ่งที่คุณทำกับโทเค็นนั้นขึ้นอยู่กับคุณตราบใดที่คุณสามารถระบุได้สำหรับการเรียก API ในอนาคต
ตอนนี้เมื่อใดก็ตามที่ SPA ต้องการโทรไปยังปลายทาง API ที่ได้รับการป้องกันใด ๆ ก็จำเป็นต้องส่งโทเค็นไปพร้อมกันใน Authorization
ส่วนหัว HTTP
Authorization: Bearer {JWT Token}
บันทึก : อีกครั้งนี่เป็นเรื่องธรรมดา JWT ไม่ได้กำหนดวิธีการเฉพาะสำหรับการส่งตัวเองไปยังเซิร์ฟเวอร์ คุณสามารถต่อท้าย URL หรือส่งในคุกกี้ก็ได้
เมื่อเซิร์ฟเวอร์ได้รับ JWT เซิร์ฟเวอร์จะสามารถถอดรหัสได้ตรวจสอบความสอดคล้องโดยใช้ข้อมูลลับที่แชร์ของ HMAC และตรวจสอบการหมดอายุโดยใช้ exp
และ nbf
ฟิลด์ นอกจากนี้ยังสามารถใช้ iss
เพื่อให้แน่ใจว่าเป็นฝ่ายที่ออกต้นฉบับของ JWT นี้
เมื่อเซิร์ฟเวอร์พอใจกับความถูกต้องของโทเค็นแล้วจะสามารถใช้ข้อมูลที่เก็บไว้ใน JWT ได้ ตัวอย่างเช่น uid
ที่เรารวมไว้จะให้ ID ของผู้ใช้ที่ทำการร้องขอ สำหรับตัวอย่างนี้เราได้รวม role
ด้วย ซึ่งช่วยให้เราสามารถตัดสินใจได้ว่าผู้ใช้ควรจะสามารถเข้าถึงปลายทางที่เฉพาะเจาะจงได้หรือไม่ (ไม่ว่าคุณจะเชื่อถือข้อมูลนี้หรือต้องการค้นหาฐานข้อมูลขึ้นอยู่กับระดับความปลอดภัยที่ต้องการ)
function getTodos(jwtString) { var token = JWTDecode(jwtstring); if( Date.now() token.exp*1000) { throw new Error('Token has expired'); } if( token.iss != 'todoapi') { throw new Error('Token not issued here'); } var userID = token.uid; var todos = loadUserTodosFromDB(userID); return JSON.stringify(todos); }
ในการทำตามคุณจะต้องมี Node.js เวอร์ชันล่าสุด (6.x หรือใหม่กว่า), npm (3.x หรือใหม่กว่า) และติดตั้ง angular-cli หากคุณต้องการติดตั้ง Node.js ซึ่งรวมถึง npm โปรดปฏิบัติตามคำแนะนำ ที่นี่ . หลังจากนั้น angular-cli
สามารถติดตั้งได้โดยใช้ npm
(หรือ yarn
หากคุณติดตั้งไว้):
การลงทุนโน้ตแปลงสภาพคืออะไร
# installation using npm npm install -g @angular/cli # installation using yarn yarn global add @angular/cli
ฉันจะไม่ลงรายละเอียดเกี่ยวกับสำเร็จรูป Angular 6 ที่เราจะใช้ที่นี่ แต่สำหรับขั้นตอนต่อไปฉันได้สร้างที่เก็บ Github เพื่อเก็บแอปพลิเคชันสิ่งที่ต้องทำขนาดเล็กเพื่อแสดงให้เห็นถึงความเรียบง่ายในการเพิ่มการตรวจสอบสิทธิ์ JWT ให้กับแอปของคุณ เพียงแค่โคลนโดยใช้สิ่งต่อไปนี้:
git clone https://github.com/sschocke/angular-jwt-todo.git cd angular-jwt-todo git checkout pre-jwt
git checkout pre-jwt
คำสั่งสลับไปยังรีลีสที่ตั้งชื่อโดยที่ JWT ยังไม่ได้ใช้งาน
ควรมีสองโฟลเดอร์ภายในเรียกว่า server
และ client
. เซิร์ฟเวอร์คือเซิร์ฟเวอร์ Node API ที่จะโฮสต์ API พื้นฐานของเรา ลูกค้าคือแอป Angular 6 ของเรา
ในการเริ่มต้นให้ติดตั้งการอ้างอิงและเริ่มเซิร์ฟเวอร์ API
cd server # installation using npm npm install # or installation using yarn yarn node app.js
คุณควรจะสามารถไปตามลิงก์เหล่านี้และรับข้อมูลเป็นตัวแทน JSON ในตอนนี้จนกว่าเราจะมีการตรวจสอบสิทธิ์เราได้ทำการฮาร์ดโค้ด /todos
จุดสิ้นสุดเพื่อส่งคืนงานสำหรับ userID=1
:
userID=1
ในการเริ่มต้นกับแอปไคลเอ็นต์เราจำเป็นต้องติดตั้งการอ้างอิงและเริ่มเซิร์ฟเวอร์การพัฒนา
cd client # using npm npm install npm start # using yarn yarn yarn start
บันทึก : ขึ้นอยู่กับความเร็วสายของคุณอาจใช้เวลาสักครู่ในการดาวน์โหลดการอ้างอิงทั้งหมด
ถ้าทุกอย่างเป็นไปด้วยดีตอนนี้คุณควรเห็นอะไรแบบนี้เมื่อไปที่ http: // localhost: 4200 :
ในการเพิ่มการรองรับการตรวจสอบสิทธิ์ JWT เราจะใช้ประโยชน์จากไลบรารีมาตรฐานบางส่วนที่มีให้ซึ่งทำให้ง่ายขึ้น แน่นอนคุณสามารถละทิ้งสิ่งอำนวยความสะดวกเหล่านี้และดำเนินการทุกอย่างได้ด้วยตัวเอง แต่นั่นอยู่นอกเหนือขอบเขตของเราที่นี่
ก่อนอื่นมาติดตั้งไลบรารีที่ฝั่งไคลเอ็นต์ ได้รับการพัฒนาและดูแลโดย Auth0 ซึ่งเป็นไลบรารีที่ให้คุณเพิ่ม การรับรองความถูกต้องบนคลาวด์ ไปยังเว็บไซต์ การใช้ห้องสมุดเองไม่จำเป็นต้องให้คุณใช้บริการของห้องสมุด
cd client # installation using npm npm install @auth0/angular-jwt # installation using yarn yarn add @auth0/angular-jwt
เราจะไปถึงโค้ดในไม่กี่วินาที แต่ในขณะที่เราอยู่ที่นั่นเรามาตั้งค่าฝั่งเซิร์ฟเวอร์กันดีกว่า เราจะใช้ body-parser
, jsonwebtoken
และ express-jwt
ไลบรารีเพื่อให้ Node เข้าใจเนื้อความ JSON POST และ JWT
cd server # installation using npm npm install body-parser jsonwebtoken express-jwt # installation using yarn yarn add body-parser jsonwebtoken express-jwt
ขั้นแรกเราต้องมีวิธีตรวจสอบสิทธิ์ผู้ใช้ก่อนที่จะให้โทเค็น สำหรับการสาธิตง่ายๆของเราเราจะเพียงแค่ตั้งค่าจุดสิ้นสุดการตรวจสอบความถูกต้องคงที่ด้วยชื่อผู้ใช้และรหัสผ่านแบบฮาร์ดโค้ด อาจเป็นเรื่องง่ายหรือซับซ้อนตามที่แอปพลิเคชันของคุณต้องการ สิ่งสำคัญคือการส่ง JWT กลับ
ใน server/app.js
เพิ่มรายการด้านล่าง require
บรรทัดดังนี้:
const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const expressJwt = require('express-jwt');
ดังต่อไปนี้:
ที่ใช้ทับทิมบนราง
app.use(bodyParser.json()); app.post('/api/auth', function(req, res) { const body = req.body; const user = USERS.find(user => user.username == body.username); if(!user || body.password != 'todo') return res.sendStatus(401); var token = jwt.sign({userID: user.id}, 'todo-app-super-shared-secret', {expiresIn: '2h'}); res.send({token}); });
ส่วนใหญ่เป็นโค้ด JavaScript พื้นฐาน เราได้รับเนื้อหา JSON ที่ส่งต่อไปยัง /auth
endpoint ค้นหาผู้ใช้ที่ตรงกับชื่อผู้ใช้นั้นตรวจสอบว่าเรามีผู้ใช้และรหัสผ่านตรงกันแล้วส่งกลับ 401 Unauthorized
ข้อผิดพลาด HTTP ถ้าไม่
ส่วนที่สำคัญคือการสร้างโทเค็นและเราจะแยกย่อยตามพารามิเตอร์สามตัว ไวยากรณ์สำหรับ sign
มีดังนี้ jwt.sign(payload, secretOrPrivateKey, [options, callback])
โดยที่:
payload
เป็นออบเจ็กต์ลิเทอรัลของคู่คีย์ - ค่าที่คุณต้องการเข้ารหัสภายในโทเค็นของคุณ จากนั้นข้อมูลนี้สามารถถอดรหัสจากโทเค็นโดยใครก็ได้ที่มีคีย์ถอดรหัส ในตัวอย่างของเราเราเข้ารหัส user.id
ดังนั้นเมื่อเราได้รับโทเค็นอีกครั้งที่ส่วนหลังสำหรับการตรวจสอบสิทธิ์เราจะรู้ว่าเรากำลังติดต่อกับผู้ใช้รายใดsecretOrPrivateKey
เป็นคีย์ลับที่ใช้ร่วมกันการเข้ารหัส HMAC ซึ่งเป็นสิ่งที่เราใช้ในแอปของเราเพื่อความเรียบง่ายหรือคีย์ส่วนตัวการเข้ารหัส RSA / ECDSAoptions
แสดงถึงตัวเลือกต่างๆที่สามารถส่งผ่านไปยังตัวเข้ารหัสในรูปแบบของคู่คีย์ - ค่า โดยปกติอย่างน้อยเราระบุ expiresIn
(กลายเป็น exp
การอ้างสิทธิ์ที่สงวนไว้) และ issuer
(iss
การอ้างสิทธิ์ที่สงวนไว้) เพื่อให้โทเค็นไม่สามารถใช้ได้ตลอดไปและเซิร์ฟเวอร์สามารถตรวจสอบได้ว่าโทเค็นได้ออกโทเค็นในความเป็นจริงแล้วcallback
เป็นฟังก์ชั่นในการเรียกใช้หลังจากเข้ารหัสเสร็จแล้วควรจัดการการเข้ารหัสโทเค็นแบบอะซิงโครนัส (คุณยังสามารถอ่านเกี่ยวกับ รายละเอียดเพิ่มเติมเกี่ยวกับ options
และวิธีใช้การเข้ารหัสคีย์สาธารณะแทนคีย์ลับที่แชร์ .)
เพื่อให้ Angular 6 ทำงานกับ JWT ของเรานั้นค่อนข้างง่ายโดยใช้ angular-jwt
เพียงเพิ่มสิ่งต่อไปนี้ใน client/src/app/app.modules.ts
:
import { JwtModule } from '@auth0/angular-jwt'; // ... export function tokenGetter() { return localStorage.getItem('access_token'); } @NgModule({ // ... imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule, // Add this import here JwtModule.forRoot({ config: { tokenGetter: tokenGetter, whitelistedDomains: ['localhost:4000'], blacklistedRoutes: ['localhost:4000/api/auth'] } }) ], // ... }
นั่นคือสิ่งที่จำเป็นโดยทั่วไป แน่นอนว่าเรามีรหัสเพิ่มเติมที่จะเพิ่มเพื่อทำการตรวจสอบความถูกต้องเบื้องต้น แต่ angular-jwt
ไลบรารีดูแลการส่งโทเค็นพร้อมกับคำขอ HTTP ทุกรายการ
tokenGetter()
ฟังก์ชั่นทำตามที่กล่าวไว้อย่างแน่นอน แต่วิธีการใช้งานนั้นขึ้นอยู่กับคุณทั้งหมด เราได้เลือกที่จะคืนโทเค็นที่เราบันทึกไว้ใน localStorage
คุณมีอิสระที่จะจัดหาวิธีการอื่นใดที่คุณต้องการตราบเท่าที่มันส่งคืนไฟล์ โทเค็นเว็บ JSON สตริงที่เข้ารหัสwhiteListedDomains
มีตัวเลือกเพื่อให้คุณสามารถ จำกัด โดเมนที่ JWT จะถูกส่งไปยัง API สาธารณะจึงไม่ได้รับ JWT ของคุณเช่นกันblackListedRoutes
ตัวเลือกช่วยให้คุณระบุเส้นทางเฉพาะที่ไม่ควรได้รับ JWT แม้ว่าเส้นทางนั้นจะอยู่ในโดเมนที่อนุญาตพิเศษก็ตาม ตัวอย่างเช่นจุดสิ้นสุดการตรวจสอบสิทธิ์ไม่จำเป็นต้องได้รับเนื่องจากไม่มีประเด็น: โดยทั่วไปโทเค็นจะเป็นโมฆะเมื่อมีการเรียกใช้ณ จุดนี้เรามีวิธีสร้าง JWT สำหรับผู้ใช้ที่กำหนดโดยใช้ /auth
จุดสิ้นสุดบน API ของเราและเราได้ทำท่อประปาบน Angular เพื่อส่ง JWT พร้อมคำขอ HTTP ทุกรายการ เยี่ยมมาก แต่คุณอาจชี้ให้เห็นว่าไม่มีอะไรเปลี่ยนแปลงสำหรับผู้ใช้ และคุณจะถูกต้อง เรายังสามารถนำทางไปยังทุกหน้าในแอปของเราและเราสามารถเรียกใช้ปลายทาง API ใดก็ได้โดยไม่ต้องส่ง JWT ไม่ดี!
เราจำเป็นต้องอัปเดตแอปไคลเอ็นต์ของเราเพื่อให้กังวลเกี่ยวกับผู้ที่เข้าสู่ระบบและอัปเดต API ของเราเพื่อต้องการ JWT มาเริ่มกันเลย.
เราต้องการส่วนประกอบ Angular ใหม่สำหรับการเข้าสู่ระบบเพื่อความกระชับฉันจะทำให้สิ่งนี้ง่ายที่สุด นอกจากนี้เรายังต้องการบริการที่จะจัดการกับข้อกำหนดการตรวจสอบสิทธิ์ทั้งหมดของเราและ ยามเชิงมุม เพื่อป้องกันเส้นทางที่ไม่ควรเข้าถึงก่อนเข้าสู่ระบบเราจะดำเนินการต่อไปนี้ในบริบทแอปพลิเคชันไคลเอนต์
cd client ng g component login --spec=false --inline-style ng g service auth --flat --spec=false ng g guard auth --flat --spec=false
สิ่งนี้ควรสร้างขึ้น สี่ ไฟล์ใหม่ใน client
โฟลเดอร์:
src/app/login/login.component.html src/app/login/login.component.ts src/app/auth.service.ts src/app/auth.guard.ts
ต่อไปเราต้องให้บริการตรวจสอบสิทธิ์และป้องกันแอปของเรา ปรับปรุง client/src/app/app.modules.ts
:
import { AuthService } from './auth.service'; import { AuthGuard } from './auth.guard'; // ... providers: [ TodoService, UserService, AuthService, AuthGuard ],
จากนั้นอัปเดตการกำหนดเส้นทางใน client/src/app/app-routing.modules.ts
เพื่อใช้ประโยชน์จากตัวป้องกันการตรวจสอบความถูกต้องและจัดหาเส้นทางสำหรับส่วนประกอบการเข้าสู่ระบบ
// ... import { LoginComponent } from './login/login.component'; import { AuthGuard } from './auth.guard'; const routes: Routes = [ { path: 'todos', component: TodoListComponent, canActivate: [AuthGuard] }, { path: 'users', component: UserListComponent, canActivate: [AuthGuard] }, { path: 'login', component: LoginComponent}, // ...
สุดท้ายอัปเดต client/src/app/auth.guard.ts
โดยมีเนื้อหาดังต่อไปนี้:
import { Injectable } from '@angular/core'; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) { } canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (localStorage.getItem('access_token')) { return true; } this.router.navigate(['login']); return false; } }
สำหรับแอปพลิเคชันสาธิตของเราเราเพียงแค่ตรวจสอบการมีอยู่ของ JWT ในพื้นที่จัดเก็บในตัวเครื่อง ในการใช้งานจริงคุณจะต้องถอดรหัสโทเค็นและตรวจสอบความถูกต้องการหมดอายุ ฯลฯ ตัวอย่างเช่นคุณสามารถใช้ JwtHelperService สำหรับสิ่งนี้.
ณ จุดนี้แอป Angular ของเราจะเปลี่ยนเส้นทางคุณไปยังหน้าเข้าสู่ระบบเสมอเนื่องจากเราไม่มีทางเข้าสู่ระบบได้มาแก้ไขกันโดยเริ่มจากบริการตรวจสอบสิทธิ์ใน client/src/app/auth.service.ts
:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() export class AuthService { constructor(private http: HttpClient) { } login(username: string, password: string): Observable { return this.http.post('/api/auth', {username: username, password: password}) .pipe( map(result => { localStorage.setItem('access_token', result.token); return true; }) ); } logout() { localStorage.removeItem('access_token'); } public get loggedIn(): boolean { return (localStorage.getItem('access_token') !== null); } }
บริการตรวจสอบสิทธิ์ของเรามีเพียงสองฟังก์ชันเท่านั้น login
และ logout
:
login
POST
s ที่ให้มา username
และ password
ไปที่ส่วนหลังของเราและตั้งค่า access_token
ใน localStorage
หากได้รับหนึ่งหลัง เพื่อความเรียบง่ายไม่มีข้อผิดพลาดในการจัดการที่นี่ logout
เพียงแค่ล้าง access_token
จาก localStorage
ซึ่งจำเป็นต้องได้รับโทเค็นใหม่ก่อนที่จะสามารถเข้าถึงอะไรได้อีกloggedIn
เป็นคุณสมบัติบูลีนที่เราสามารถใช้เพื่อตรวจสอบได้อย่างรวดเร็วว่าผู้ใช้เข้าสู่ระบบหรือไม่และสุดท้ายองค์ประกอบการเข้าสู่ระบบ สิ่งเหล่านี้ไม่มีความเกี่ยวข้องกับการทำงานกับ JWT ดังนั้นอย่าลังเลที่จะคัดลอกและวางลงใน client/src/app/login/login.components.html
:
{{error}}
Username Password Login
และ client/src/app/login/login.components.ts
จะต้อง:
import { Component, OnInit } from '@angular/core'; import { AuthService } from '../auth.service'; import { Router } from '@angular/router'; import { first } from 'rxjs/operators'; @Component({ selector: 'app-login', templateUrl: './login.component.html' }) export class LoginComponent { public username: string; public password: string; public error: string; constructor(private auth: AuthService, private router: Router) { } public submit() { this.auth.login(this.username, this.password) .pipe(first()) .subscribe( result => this.router.navigate(['todos']), err => this.error = 'Could not authenticate' ); } }
เอาล่ะตัวอย่างการเข้าสู่ระบบ Angular 6 ของเรา:
ในขั้นตอนนี้เราควรจะสามารถเข้าสู่ระบบได้ (โดยใช้ jemma
, paul
หรือ sebastian
ด้วยรหัสผ่าน todo
) และดูหน้าจอทั้งหมดอีกครั้ง แต่แอปพลิเคชันของเราแสดงส่วนหัวการนำทางเดียวกันและไม่มีวิธีออกจากระบบโดยไม่คำนึงถึงสถานะปัจจุบัน มาแก้ไขกันก่อนที่เราจะไปแก้ไข API ของเรา
วิธีทำวิดีโอในอาฟเตอร์เอฟเฟกต์
ใน client/src/app/app.component.ts
ให้แทนที่ทั้งไฟล์ด้วยสิ่งต่อไปนี้:
import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { AuthService } from './auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private auth: AuthService, private router: Router) { } logout() { this.auth.logout(); this.router.navigate(['login']); } }
และสำหรับ client/src/app/app.component.html
แทนที่สิ่งเหล่านี้ด้วยสิ่งต่อไปนี้:
Todo List Users Login Logout
เราได้แจ้งให้ทราบบริบทการนำทางของเราว่าควรแสดงเฉพาะบางรายการขึ้นอยู่กับว่าผู้ใช้ล็อกอินหรือไม่ auth.loggedIn
แน่นอนว่าสามารถใช้ได้ทุกที่ที่คุณสามารถนำเข้าบริการตรวจสอบสิทธิ์ได้
คุณอาจกำลังคิดว่า มันยอดเยี่ยมมาก ... ทุกอย่างดูเหมือนจะทำงานได้อย่างยอดเยี่ยม . แต่ลองลงชื่อเข้าใช้ด้วยชื่อผู้ใช้ทั้งสามชื่อที่แตกต่างกันแล้วคุณจะสังเกตเห็นบางสิ่งบางอย่าง: พวกเขาทั้งหมดจะแสดงรายการสิ่งที่ต้องทำเหมือนกัน หากเราดูที่เซิร์ฟเวอร์ API ของเราเราจะเห็นว่าในความเป็นจริงผู้ใช้แต่ละคนมีรายการของตัวเองแล้วมีอะไรเกิดขึ้นบ้าง?
จำไว้ว่าเมื่อเราเริ่มต้นเราเขียนโค้ด /todos
ของเรา ปลายทาง API เพื่อส่งคืนรายการสิ่งที่ต้องทำสำหรับ userID=1
เสมอ เนื่องจากเราไม่มีทางรู้ได้เลยว่าใครเป็นผู้ใช้ที่ลงชื่อเข้าใช้อยู่
นิติบุคคลประเภท s หรือ c
ตอนนี้เราทำแล้วมาดูกันว่าการรักษาความปลอดภัยปลายทางของเรานั้นง่ายเพียงใดและใช้ข้อมูลที่เข้ารหัสใน JWT เพื่อระบุตัวตนของผู้ใช้ที่ต้องการ เริ่มแรกให้เพิ่มบรรทัดนี้ใน server/app.js
ของคุณ ใต้ไฟล์สุดท้าย app.use()
โทร:
app.use(expressJwt({secret: 'todo-app-super-shared-secret'}).unless({path: ['/api/auth']}));
เราใช้ express-jwt
มิดเดิลแวร์บอกความลับที่แชร์คืออะไรและระบุอาร์เรย์ของเส้นทางที่ไม่ควรต้องใช้ JWT สำหรับ เท่านี้ก็เรียบร้อย ไม่จำเป็นต้องสัมผัสทุกจุดสิ้นสุดสร้าง if
งบทั้งหมดหรืออะไรก็ได้
ภายในมิดเดิลแวร์กำลังตั้งสมมติฐานบางประการ ตัวอย่างเช่นสมมติว่า Authorization
ส่วนหัว HTTP เป็นไปตามรูปแบบ JWT ทั่วไปของ Bearer {token}
(ห้องสมุดมีตัวเลือกมากมายสำหรับการปรับแต่งวิธีการทำงานหากไม่เป็นเช่นนั้นดู express-jwt การใช้งาน สำหรับรายละเอียดเพิ่มเติม)
วัตถุประสงค์ประการที่สองของเราคือใช้ข้อมูลที่เข้ารหัส JWT เพื่อค้นหาว่าใครเป็นผู้โทรออก อีกครั้ง express-jwt
มาช่วย ในฐานะที่เป็นส่วนหนึ่งของการอ่านโทเค็นและการตรวจสอบมันจะตั้งค่าเพย์โหลดที่เข้ารหัสที่เราส่งไปในกระบวนการเซ็นชื่อให้กับตัวแปร req.user
ใน Express. จากนั้นเราสามารถใช้เพื่อเข้าถึงตัวแปรใด ๆ ที่เราเก็บไว้ได้ทันที ในกรณีของเราเราตั้ง userID
เท่ากับ ID ของผู้ใช้ที่ได้รับการพิสูจน์ตัวตนและด้วยเหตุนี้เราจึงสามารถใช้เป็น req.user.userID
ได้โดยตรง
ปรับปรุง server/app.js
อีกครั้งและเปลี่ยน /todos
จุดสิ้นสุดที่จะอ่านดังนี้:
res.send(getTodos(req.user.userID));
เท่านี้ก็เรียบร้อย ขณะนี้ API ของเราปลอดภัยจากการเข้าถึงโดยไม่ได้รับอนุญาตและเราสามารถระบุได้อย่างปลอดภัยว่าผู้ใช้ที่ได้รับการรับรองความถูกต้องของเราคือใครในปลายทางใด ๆ แอปพลิเคชันไคลเอนต์ของเรายังมีกระบวนการตรวจสอบสิทธิ์ที่เรียบง่ายและบริการ HTTP ใด ๆ ที่เราเขียนเรียกว่าปลายทาง API ของเราจะมีโทเค็นการตรวจสอบสิทธิ์แนบมาโดยอัตโนมัติ
หากคุณโคลนที่เก็บ Github และเพียงแค่ต้องการเห็นผลลัพธ์สุดท้ายในการดำเนินการคุณสามารถตรวจสอบโค้ดในรูปแบบสุดท้ายโดยใช้:
git checkout with-jwt
ฉันหวังว่าคุณจะพบว่าคำแนะนำนี้มีประโยชน์สำหรับการเพิ่ม การพิสูจน์ตัวตน JWT ไปยังแอป Angular ของคุณเอง ขอบคุณที่อ่าน!
ที่เกี่ยวข้อง: JSON Web Token Tutorial: ตัวอย่างใน Laravel และ AngularJSในแง่การเขียนโปรแกรมโทเค็นมีคำจำกัดความมากมาย ในด้านความปลอดภัยและการตรวจสอบสิทธิ์เป็นเพียงสตริงทึบแสงที่เข้ารหัสข้อมูลจำนวนเล็กน้อยที่สามารถส่งและจัดเก็บได้ง่าย แต่แทบจะไม่มีโอกาสที่จะชนกับโทเค็นอื่น ๆ
การพิสูจน์ตัวตนโดยใช้โทเค็นหมายถึงการเข้ารหัสข้อมูลที่จำเป็นในการพิสูจน์ตัวตนและอนุญาตให้ผู้ใช้เป็นโทเค็นจากนั้นใช้โทเค็นที่เซ็นชื่อแบบเข้ารหัสเพื่อการอนุญาตแทนการใช้ชื่อผู้ใช้ / รหัสผ่านร่วมกันเมื่อเข้าถึงทรัพยากรที่มีการป้องกัน
JWT เป็นคำย่อของ JSON Web Token ซึ่งโดยทั่วไปหมายถึงออบเจ็กต์ JSON ที่มีส่วนหัวน้ำหนักบรรทุกและลายเซ็น เป็นวิธีที่ปลอดภัยในการแลกเปลี่ยนข้อมูลการรับรองความถูกต้องระหว่างสองฝ่ายผ่านเครือข่ายเมื่อใช้ร่วมกับเทคโนโลยีอื่น ๆ เช่น SSL
OAuth 2.0 เป็นโปรโตคอลมาตรฐานสำหรับดำเนินการให้สิทธิ์ในแพลตฟอร์มที่รองรับจำนวนมาก ตัวอย่างเช่น Google, Facebook และ OpenID ล้วนใช้ OAuth 2.0 กำหนดขั้นตอนและรูปแบบที่จะปฏิบัติตามเพื่อตรวจสอบสิทธิ์กับเซิร์ฟเวอร์การตรวจสอบความถูกต้องที่จัดเตรียมไว้และเข้าถึงทรัพยากรที่ได้รับการป้องกัน