كيفية إنشاء واجهة برمجة تطبيقات تستند إلى الدور باستخدام مصادقة Firebase
نشرت: 2022-03-11في هذا البرنامج التعليمي ، سننشئ واجهة برمجة تطبيقات REST لإدارة المستخدمين والأدوار باستخدام Firebase و Node.js. بالإضافة إلى ذلك ، سنرى كيفية استخدام واجهة برمجة التطبيقات (API) لتفويض (أو عدم السماح) للمستخدمين الذين يمكنهم الوصول إلى موارد محددة.
مقدمة
يتطلب كل تطبيق تقريبًا مستوى معينًا من نظام التفويض. في بعض الحالات ، يكون التحقق من صحة اسم المستخدم / كلمة المرور المعينة من خلال جدول المستخدمين كافيًا ، ولكن في كثير من الأحيان ، نحتاج إلى نموذج أذونات أكثر دقة للسماح لبعض المستخدمين بالوصول إلى موارد معينة وتقييدهم من الآخرين. إن بناء نظام لدعم هذا الأخير ليس بالأمر الهين ويمكن أن يستغرق وقتًا طويلاً. في هذا البرنامج التعليمي ، سوف نتعلم كيفية إنشاء واجهة برمجة تطبيقات مصادقة تستند إلى الأدوار باستخدام Firebase ، مما سيساعدنا على العمل بسرعة.
المصادقة على أساس الدور
في نموذج التفويض هذا ، يتم منح حق الوصول إلى الأدوار ، بدلاً من مستخدمين محددين ، ويمكن للمستخدم الحصول على واحد أو أكثر بناءً على كيفية تصميم نموذج الإذن الخاص بك. من ناحية أخرى ، تتطلب الموارد أدوارًا معينة للسماح للمستخدم بتنفيذها.
Firebase
مصادقة Firebase
باختصار ، Firebase Authentication عبارة عن نظام مصادقة قابل للتوسيع قائم على الرمز المميز ويوفر عمليات تكامل خارج الصندوق مع أكثر المزودين شيوعًا مثل Google و Facebook و Twitter ، من بين آخرين.
إنها تمكننا من استخدام المطالبات المخصصة التي سنستفيد منها لبناء واجهة برمجة تطبيقات مرنة قائمة على الأدوار.
يمكننا تعيين أي قيمة JSON في المطالبات (على سبيل المثال ، { role: 'admin' }
أو { role: 'manager' }
).
بمجرد التعيين ، سيتم تضمين المطالبات المخصصة في الرمز المميز الذي ينشئه Firebase ، ويمكننا قراءة القيمة للتحكم في الوصول.
كما أنه يأتي مع حصة مجانية سخية للغاية ، والتي ستكون في معظم الحالات أكثر من كافية.
وظائف Firebase
الوظائف عبارة عن خدمة نظام أساسي بدون خادم تتم إدارته بالكامل. نحتاج فقط إلى كتابة الكود الخاص بنا في Node.js ونشره. يعتني Firebase بتوسيع نطاق البنية التحتية عند الطلب وتهيئة الخادم والمزيد. في حالتنا ، سنستخدمها لبناء واجهة برمجة التطبيقات الخاصة بنا وكشفها عبر HTTP على الويب.
يسمح لنا Firebase بتعيين تطبيقات express.js
لمسارات مختلفة - على سبيل المثال ، يمكنك إنشاء تطبيق Express وربطه بـ /mypath
، وسيتم التعامل مع جميع الطلبات الواردة إلى هذا المسار بواسطة app
الذي تم تكوينه.
من سياق الوظيفة ، يمكنك الوصول إلى Firebase Authentication API بالكامل ، باستخدام Admin SDK.
هذه هي الطريقة التي سننشئ بها واجهة برمجة تطبيقات المستخدم.
ما سنبنيه
لذا قبل أن نبدأ ، دعونا نلقي نظرة على ما سنبنيه. سنقوم بإنشاء واجهة برمجة تطبيقات REST بنقاط النهاية التالية:
Http فعل | طريق | وصف | تفويض |
---|---|---|---|
احصل على | / المستخدمين | يسرد جميع المستخدمين | يمكن للمسؤولين والمديرين فقط الوصول |
بريد | / المستخدمين | ينشئ مستخدم جديد | يمكن للمسؤولين والمديرين فقط الوصول |
احصل على | / المستخدمين /: معرف | يحصل على: معرف المستخدم | يمكن للمسؤولين والمديرين والمستخدم نفسه مثل: معرف الوصول |
رقعة قماشية | / المستخدمين /: معرف | يحدّث: مستخدم المعرّف | يمكن للمسؤولين والمديرين والمستخدم نفسه مثل: معرف الوصول |
حذف | / المستخدمين /: معرف | يحذف: معرف المستخدم | يمكن للمسؤولين والمديرين والمستخدم نفسه مثل: معرف الوصول |
ستتعامل كل نقطة من نقاط النهاية هذه مع المصادقة ، والتحقق من صحة التفويض ، وتنفيذ عملية المراسلة ، وإرجاع رمز HTTP في النهاية.
سننشئ وظائف المصادقة والتفويض المطلوبة للتحقق من صحة الرمز المميز والتحقق مما إذا كانت المطالبات تحتوي على الدور المطلوب لتنفيذ العملية.
بناء API
من أجل بناء API ، سنحتاج:
- مشروع Firebase
- تركيب
firebase-tools
أولاً ، سجّل الدخول إلى Firebase:
firebase login
بعد ذلك ، قم بتهيئة مشروع Functions:
firebase init ? Which Firebase CLI features do you want to set up for this folder? ... (O) Functions: Configure and deploy Cloud Functions ? Select a default Firebase project for this directory: {your-project} ? What language would you like to use to write Cloud Functions? TypeScript ? Do you want to use TSLint to catch probable bugs and enforce style? Yes ? Do you want to install dependencies with npm now? Yes
في هذه المرحلة ، سيكون لديك مجلد وظائف ، مع الحد الأدنى من الإعداد لإنشاء وظائف Firebase.
في src/index.ts
، يوجد مثال helloWorld
، والذي يمكنك إلغاء التعليق عليه للتحقق من أن وظائفك تعمل. ثم يمكنك تشغيل cd functions
وتشغيل npm run serve
. سيقوم هذا الأمر بتحويل الشفرة وبدء الخادم المحلي.
يمكنك التحقق من النتائج على http: // localhost: 5000 / {your-project} / us-central1 / helloWorld
لاحظ أن الوظيفة مكشوفة على المسار المحدد باسمها في 'index.ts: 'helloWorld'
.
إنشاء وظيفة Firebase HTTP
الآن دعنا نبرمج API الخاص بنا. سنقوم بإنشاء دالة http Firebase وربطها بمسار /api
.
أولاً ، قم بتثبيت npm install express
.
في src/index.ts
سنقوم بما يلي:
- قم بتهيئة وحدة Firebase-admin SDK باستخدام
admin.initializeApp();
- قم بتعيين أحد تطبيقات Express كمعالج لنقطة نهاية https
api
الخاصة بنا
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; import * as express from 'express'; admin.initializeApp(); const app = express(); export const api = functions.https.onRequest(app);
الآن ، سيتم التعامل مع جميع الطلبات التي تنتقل إلى /api
بواسطة مثيل app
.
الشيء التالي الذي سنفعله هو تكوين مثيل app
لدعم CORS وإضافة برمجية وسيطة JSON body parser. بهذه الطريقة يمكننا تقديم طلبات من أي عنوان URL وتحليل الطلبات بتنسيق JSON.
سنقوم أولاً بتثبيت التبعيات المطلوبة.
npm install --save cors body-parser
npm install --save-dev @types/cors
وثم:
//... import * as cors from 'cors'; import * as bodyParser from 'body-parser'; //... const app = express(); app.use(bodyParser.json()); app.use(cors({ origin: true })); export const api = functions.https.onRequest(app);
أخيرًا ، سنقوم بتهيئة المسارات التي سيتعامل معها app
.
//... import { routesConfig } from './users/routes-config'; //… app.use(cors({ origin: true })); routesConfig(app) export const api = functions.https.onRequest(app);
تتيح لنا وظائف Firebase تعيين تطبيق Express باعتباره المعالج ، وأي مسار بعد الذي قمت بإعداده في functions.https.onRequest(app);
—في هذه الحالة ، api
سيتم التعامل معها أيضًا بواسطة app
. يتيح لنا ذلك كتابة نقاط نهاية محددة مثل api/users
وتعيين معالج لكل فعل HTTP ، وهو ما سنفعله بعد ذلك.
لنقم بإنشاء ملف src/users/routes-config.ts
هنا ، سنقوم بتعيين معالج create
في POST '/users'
import { Application } from "express"; import { create} from "./controller"; export function routesConfig(app: Application) { app.post('/users', create ); }
الآن ، سننشئ ملف src/users/controller.ts
.
في هذه الوظيفة ، نتحقق أولاً من أن جميع الحقول موجودة في الطلب الأساسي ، وبعد ذلك ، نقوم بإنشاء المستخدم وتعيين المطالبات المخصصة.
لقد نجحنا للتو في تمرير { role }
في setCustomUserClaims
— تم تعيين الحقول الأخرى بالفعل بواسطة Firebase.
في حالة عدم حدوث أخطاء ، نعيد الرمز 201 مع إنشاء uid
المستخدم الخاص بالمستخدم.
import { Request, Response } from "express"; import * as admin from 'firebase-admin' export async function create(req: Request, res: Response) { try { const { displayName, password, email, role } = req.body if (!displayName || !password || !email || !role) { return res.status(400).send({ message: 'Missing fields' }) } const { uid } = await admin.auth().createUser({ displayName, password, email }) await admin.auth().setCustomUserClaims(uid, { role }) return res.status(201).send({ uid }) } catch (err) { return handleError(res, err) } } function handleError(res: Response, err: any) { return res.status(500).send({ message: `${err.code} - ${err.message}` }); }
الآن ، دعنا نؤمن المعالج عن طريق إضافة التفويض. للقيام بذلك ، سنضيف اثنين من المعالجات إلى نقطة نهاية create
الخاصة بنا. باستخدام express.js
، يمكنك تعيين سلسلة من المعالجات التي سيتم تنفيذها بالترتيب. داخل المعالج ، يمكنك تنفيذ التعليمات البرمجية وتمريرها إلى المعالج next()
أو إرجاع استجابة. ما سنفعله هو المصادقة على المستخدم أولاً ثم التحقق مما إذا كان مصرحًا له بالتنفيذ.
في ملف src/users/routes-config.ts
:
//... import { isAuthenticated } from "../auth/authenticated"; import { isAuthorized } from "../auth/authorized"; export function routesConfig(app: Application) { app.post('/users', isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), create ); }
لنقم بإنشاء الملفات src/auth/authenticated.ts
.
في هذه الوظيفة ، سنقوم بالتحقق من وجود الرمز المميز لحامل authorization
في رأس الطلب. ثم سنقوم بفك تشفيرها باستخدام admin.auth().verifyidToken()
في استخدام معرّف المستخدم role
email
في متغير uid
، والذي res.locals
لاحقًا للتحقق من التفويض.
في حالة عدم صلاحية الرمز المميز ، نرجع استجابة 401 إلى العميل:
import { Request, Response } from "express"; import * as admin from 'firebase-admin' export async function isAuthenticated(req: Request, res: Response, next: Function) { const { authorization } = req.headers if (!authorization) return res.status(401).send({ message: 'Unauthorized' }); if (!authorization.startsWith('Bearer')) return res.status(401).send({ message: 'Unauthorized' }); const split = authorization.split('Bearer ') if (split.length !== 2) return res.status(401).send({ message: 'Unauthorized' }); const token = split[1] try { const decodedToken: admin.auth.DecodedIdToken = await admin.auth().verifyIdToken(token); console.log("decodedToken", JSON.stringify(decodedToken)) res.locals = { ...res.locals, uid: decodedToken.uid, role: decodedToken.role, email: decodedToken.email } return next(); } catch (err) { console.error(`${err.code} - ${err.message}`) return res.status(401).send({ message: 'Unauthorized' }); } }
الآن ، دعنا ننشئ ملف src/auth/authorized.ts
في هذا المعالج ، نستخرج معلومات المستخدم من res.locals
الذي قمنا بتعيينه مسبقًا ونتحقق مما إذا كان لديه الدور المطلوب لتنفيذ العملية أو في حالة سماح العملية لنفس المستخدم بالتنفيذ ، فإننا نتحقق من صحة المعرف في معلمات الطلب هو نفسه الموجود في رمز المصادقة. إذا لم يكن لدى المستخدم الدور المطلوب ، فسنقوم بإرجاع 403.
import { Request, Response } from "express"; export function isAuthorized(opts: { hasRole: Array<'admin' | 'manager' | 'user'>, allowSameUser?: boolean }) { return (req: Request, res: Response, next: Function) => { const { role, email, uid } = res.locals const { id } = req.params if (opts.allowSameUser && id && uid === id) return next(); if (!role) return res.status(403).send(); if (opts.hasRole.includes(role)) return next(); return res.status(403).send(); } }
باستخدام هاتين الطريقتين ، سنتمكن من مصادقة الطلبات وتفويضها نظرًا role
في الرمز المميز الوارد. هذا رائع ، ولكن نظرًا لأن Firebase لا يسمح لنا بتعيين مطالبات مخصصة من وحدة تحكم المشروع ، فلن نتمكن من تنفيذ أي من نقاط النهاية هذه. لتجاوز ذلك ، يمكننا إنشاء مستخدم جذر من Firebase Authentication Console
وقم بتعيين مقارنة بريد إلكتروني في الكود. الآن ، عند طرد الطلبات من هذا المستخدم ، سنكون قادرين على تنفيذ جميع العمليات.
//... const { role, email, uid } = res.locals const { id } = req.params if (email === '[email protected]') return next(); //...
الآن ، دعنا نضيف باقي عمليات CRUD إلى src/users/routes-config.ts
.
بالنسبة للعمليات للحصول على مستخدم واحد أو تحديثه حيث يتم إرسال :id
param ، فإننا نسمح أيضًا لنفس المستخدم بتنفيذ العملية.
export function routesConfig(app: Application) { //.. // lists all users app.get('/users', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), all ]); // get :id user app.get('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'], allowSameUser: true }), get ]); // updates :id user app.patch('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'], allowSameUser: true }), patch ]); // deletes :id user app.delete('/users/:id', [ isAuthenticated, isAuthorized({ hasRole: ['admin', 'manager'] }), remove ]); }
وعلى src/users/controller.ts
. في هذه العمليات ، نستفيد من SDK للمشرف للتفاعل مع مصادقة Firebase وتنفيذ العمليات ذات الصلة. كما فعلنا سابقًا في عملية create
، نعيد رمز HTTP ذي مغزى في كل عملية.
بالنسبة لعملية التحديث ، نقوم بالتحقق من صحة جميع الحقول الموجودة وإلغاء customClaims
بتلك المرسلة في الطلب:
//.. export async function all(req: Request, res: Response) { try { const listUsers = await admin.auth().listUsers() const users = listUsers.users.map(mapUser) return res.status(200).send({ users }) } catch (err) { return handleError(res, err) } } function mapUser(user: admin.auth.UserRecord) { const customClaims = (user.customClaims || { role: '' }) as { role?: string } const role = customClaims.role ? customClaims.role : '' return { uid: user.uid, email: user.email || '', displayName: user.displayName || '', role, lastSignInTime: user.metadata.lastSignInTime, creationTime: user.metadata.creationTime } } export async function get(req: Request, res: Response) { try { const { id } = req.params const user = await admin.auth().getUser(id) return res.status(200).send({ user: mapUser(user) }) } catch (err) { return handleError(res, err) } } export async function patch(req: Request, res: Response) { try { const { id } = req.params const { displayName, password, email, role } = req.body if (!id || !displayName || !password || !email || !role) { return res.status(400).send({ message: 'Missing fields' }) } await admin.auth().updateUser(id, { displayName, password, email }) await admin.auth().setCustomUserClaims(id, { role }) const user = await admin.auth().getUser(id) return res.status(204).send({ user: mapUser(user) }) } catch (err) { return handleError(res, err) } } export async function remove(req: Request, res: Response) { try { const { id } = req.params await admin.auth().deleteUser(id) return res.status(204).send({}) } catch (err) { return handleError(res, err) } } //...
الآن يمكننا تشغيل الوظيفة محليًا. للقيام بذلك ، تحتاج أولاً إلى إعداد مفتاح الحساب لتتمكن من الاتصال بواجهة برمجة تطبيقات المصادقة محليًا. ثم اركض:

npm run serve
انشر API
رائعة! الآن بعد أن كتبنا واجهة برمجة التطبيقات القائمة على الأدوار ، يمكننا نشرها على الويب والبدء في استخدامها. يعد النشر باستخدام Firebase أمرًا سهلاً للغاية ، فنحن نحتاج فقط إلى تشغيل firebase deploy
. بمجرد اكتمال النشر ، يمكننا الوصول إلى API الخاص بنا على عنوان URL المنشور.
يمكنك التحقق من عنوان URL لواجهة برمجة التطبيقات على https://console.firebase.google.com/u/0/project/{your-project}/functions/list.
في حالتي ، إنه [https://us-central1-joaq-lab.cloudfunctions.net/api].
تستهلك API
بمجرد نشر واجهة برمجة التطبيقات الخاصة بنا ، لدينا عدة طرق لاستخدامها - في هذا البرنامج التعليمي ، سأغطي كيفية استخدامها عبر Postman أو من تطبيق Angular.
إذا أدخلنا قائمة URL All Users ( /api/users
) على أي متصفح ، فسنحصل على ما يلي:
والسبب في ذلك هو عند إرسال الطلب من مستعرض ، فإننا نقوم بتنفيذ طلب GET بدون رؤوس مصادقة. هذا يعني أن API الخاص بنا يعمل بالفعل كما هو متوقع!
يتم تأمين واجهة برمجة التطبيقات الخاصة بنا عبر الرموز المميزة - من أجل إنشاء مثل هذا الرمز المميز ، نحتاج إلى الاتصال بـ Firebase's Client SDK وتسجيل الدخول باستخدام بيانات اعتماد صالحة للمستخدم / كلمة المرور. عند النجاح ، سيرسل Firebase رمزًا مميزًا في الاستجابة يمكننا بعد ذلك إضافته إلى رأس أي طلب تالي نريد تنفيذه.
من تطبيق Angular
في هذا البرنامج التعليمي ، سأنتقل إلى الأجزاء المهمة لاستهلاك واجهة برمجة التطبيقات من تطبيق Angular. يمكن الوصول إلى المستودع الكامل هنا ، وإذا كنت بحاجة إلى برنامج تعليمي خطوة بخطوة حول كيفية إنشاء تطبيق Angular وتكوين @ angular / fire للاستخدام ، فيمكنك التحقق من هذا المنشور.
لذا ، بالعودة إلى تسجيل الدخول ، سيكون لدينا SignInComponent
مع <form>
للسماح للمستخدم بإدخال اسم مستخدم وكلمة مرور.
//... <form [formGroup]="form"> <div class="form-group"> <label>Email address</label> <input type="email" formControlName="email" class="form-control" placeholder="Enter email"> </div> <div class="form-group"> <label>Password</label> <input type="password" formControlName="password" class="form-control" placeholder="Password"> </div> </form> //...
وفي الفصل ، نقوم بتسجيل الدخول مع AngularFireAuth
signInWithEmailAndPassword
//... form: FormGroup = new FormGroup({ email: new FormControl(''), password: new FormControl('') }) constructor( private afAuth: AngularFireAuth ) { } async signIn() { try { const { email, password } = this.form.value await this.afAuth.auth.signInWithEmailAndPassword(email, password) } catch (err) { console.log(err) } } //..
في هذه المرحلة ، يمكننا تسجيل الدخول إلى مشروع Firebase.
وعندما نفحص طلبات الشبكة في DevTools ، يمكننا أن نرى أن Firebase يُرجع رمزًا مميزًا بعد التحقق من المستخدم وكلمة المرور.
هذا الرمز هو الرمز الذي سنستخدمه لإرسال طلب الرأس إلى واجهة برمجة التطبيقات التي أنشأناها. تتمثل إحدى طرق إضافة الرمز المميز إلى جميع الطلبات في استخدام HttpInterceptor
.
يوضح هذا الملف كيفية الحصول على الرمز المميز من AngularFireAuth
وإضافته إلى طلب الرأس. ثم نقوم بتوفير ملف المعترض في AppModule.
http-interceptors / auth-token.interceptor.ts
@Injectable({ providedIn: 'root' }) export class AuthTokenHttpInterceptor implements HttpInterceptor { constructor( private auth: AngularFireAuth ) { } intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return this.auth.idToken.pipe( take(1), switchMap(idToken => { let clone = req.clone() if (idToken) { clone = clone.clone({ headers: req.headers.set('Authorization', 'Bearer ' + idToken) }); } return next.handle(clone) }) ) } } export const AuthTokenHttpInterceptorProvider = { provide: HTTP_INTERCEPTORS, useClass: AuthTokenHttpInterceptor, multi: true }
app.module.ts
@NgModule({ //.. providers: [ AuthTokenHttpInterceptorProvider ] //... }) export class AppModule { }
بمجرد تعيين المعترض ، يمكننا تقديم طلبات إلى واجهة برمجة التطبيقات الخاصة بنا من httpClient
. على سبيل المثال ، إليك خدمة UsersService
حيث نطلق على القائمة جميع المستخدمين ، ونحصل على المستخدم من خلال معرفه ، وأنشئ مستخدمًا ، وقم بتحديث مستخدم.
//… export type CreateUserRequest = { displayName: string, password: string, email: string, role: string } export type UpdateUserRequest = { uid: string } & CreateUserRequest @Injectable({ providedIn: 'root' }) export class UserService { private baseUrl = '{your-functions-url}/api/users' constructor( private http: HttpClient ) { } get users$(): Observable<User[]> { return this.http.get<{ users: User[] }>(`${this.baseUrl}`).pipe( map(result => { return result.users }) ) } user$(id: string): Observable<User> { return this.http.get<{ user: User }>(`${this.baseUrl}/${id}`).pipe( map(result => { return result.user }) ) } create(user: CreateUserRequest) { return this.http.post(`${this.baseUrl}`, user) } edit(user: UpdateUserRequest) { return this.http.patch(`${this.baseUrl}/${user.uid}`, user) } }
الآن ، يمكننا استدعاء API للحصول على المستخدم الذي قام بتسجيل الدخول من خلال معرفه وسرد جميع المستخدمين من مكون مثل هذا:
//... <div *ngIf="user$ | async; let user" class="col-12"> <div class="d-flex justify-content-between my-3"> <h4> Me </h4> </div> <ul class="list-group"> <li class="list-group-item d-flex justify-content-between align-items-center"> <div> <h5 class="mb-1">{{user.displayName}}</h5> <small>{{user.email}}</small> </div> <span class="badge badge-primary badge-pill">{{user.role?.toUpperCase()}}</span> </li> </ul> </div> <div class="col-12"> <div class="d-flex justify-content-between my-3"> <h4> All Users </h4> </div> <ul *ngIf="users$ | async; let users" class="list-group"> <li *ngFor="let user of users" class="list-group-item d-flex justify-content-between align-items-center"> <div> <h5 class="mb-1">{{user.displayName}}</h5> <small class="d-block">{{user.email}}</small> <small class="d-block">{{user.uid}}</small> </div> <span class="badge badge-primary badge-pill">{{user.role?.toUpperCase()}}</span> </li> </ul> //...
//... users$: Observable<User[]> user$: Observable<User> constructor( private userService: UserService, private userForm: UserFormService, private modal: NgbModal, private afAuth: AngularFireAuth ) { } ngOnInit() { this.users$ = this.userService.users$ this.user$ = this.afAuth.user.pipe( filter(user => !!user), switchMap(user => this.userService.user$(user.uid)) ) } //...
وهذه هي النتيجة.
لاحظ أنه إذا قمنا بتسجيل الدخول باستخدام مستخدم ذي role=user
، فسيتم عرض قسم "أنا" فقط.
وسنحصل على 403 لمفتش الشبكة. هذا بسبب القيود التي وضعناها من قبل على واجهة برمجة التطبيقات للسماح فقط "للمشرفين" بإدراج جميع المستخدمين.
الآن ، دعنا نضيف وظيفتي "إنشاء مستخدم" و "تعديل المستخدم". للقيام بذلك ، لنقم أولاً بإنشاء UserFormComponent
و UserFormService
.
<ng-container *ngIf="user$ | async"></ng-container> <div class="modal-header"> <h4 class="modal-title">{{ title$ | async}}</h4> <button type="button" class="close" (click)="dismiss()"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form [formGroup]="form" (ngSubmit)="save()"> <div class="form-group"> <label>Email address</label> <input type="email" formControlName="email" class="form-control" placeholder="Enter email"> </div> <div class="form-group"> <label>Password</label> <input type="password" formControlName="password" class="form-control" placeholder="Password"> </div> <div class="form-group"> <label>Display Name</label> <input type="string" formControlName="displayName" class="form-control" placeholder="Enter display name"> </div> <div class="form-group"> <label>Role</label> <select class="custom-select" formControlName="role"> <option value="admin">Admin</option> <option value="manager">Manager</option> <option value="user">User</option> </select> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-danger" (click)="dismiss()">Cancel</button> <button type="button" class="btn btn-primary" (click)="save()">Save</button> </div>
@Component({ selector: 'app-user-form', templateUrl: './user-form.component.html', styleUrls: ['./user-form.component.scss'] }) export class UserFormComponent implements OnInit { form = new FormGroup({ uid: new FormControl(''), email: new FormControl(''), displayName: new FormControl(''), password: new FormControl(''), role: new FormControl(''), }); title$: Observable<string>; user$: Observable<{}>; constructor( public modal: NgbActiveModal, private userService: UserService, private userForm: UserFormService ) { } ngOnInit() { this.title$ = this.userForm.title$; this.user$ = this.userForm.user$.pipe( tap(user => { if (user) { this.form.patchValue(user); } else { this.form.reset({}); } }) ); } dismiss() { this.modal.dismiss('modal dismissed'); } save() { const { displayName, email, role, password, uid } = this.form.value; this.modal.close({ displayName, email, role, password, uid }); } }
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class UserFormService { _BS = new BehaviorSubject({ title: '', user: {} }); constructor() { } edit(user) { this._BS.next({ title: 'Edit User', user }); } create() { this._BS.next({ title: 'Create User', user: null }); } get title$() { return this._BS.asObservable().pipe( map(uf => uf.title) ); } get user$() { return this._BS.asObservable().pipe( map(uf => uf.user) ); } }
مرة أخرى في المكون الرئيسي ، دعنا نضيف الأزرار لاستدعاء تلك الإجراءات. في هذه الحالة ، سيكون "تحرير المستخدم" متاحًا فقط للمستخدم الذي قام بتسجيل الدخول. يمكنك المضي قدمًا وإضافة الوظيفة لتحرير المستخدمين الآخرين إذا كنت بحاجة إلى ذلك!
//... <div class="d-flex justify-content-between my-3"> <h4> Me </h4> <button class="btn btn-primary" (click)="edit(user)"> Edit Profile </button> </div> //... <div class="d-flex justify-content-between my-3"> <h4> All Users </h4> <button class="btn btn-primary" (click)="create()"> New User </button> </div> //...
//... create() { this.userForm.create(); const modalRef = this.modal.open(UserFormComponent); modalRef.result.then(user => { this.userService.create(user).subscribe(_ => { console.log('user created'); }); }).catch(err => { }); } edit(userToEdit) { this.userForm.edit(userToEdit); const modalRef = this.modal.open(UserFormComponent); modalRef.result.then(user => { this.userService.edit(user).subscribe(_ => { console.log('user edited'); }); }).catch(err => { }); }
من ساعي البريد
Postman هي أداة لبناء وتقديم طلبات لواجهات برمجة التطبيقات. بهذه الطريقة ، يمكننا محاكاة أننا نتصل بواجهة برمجة التطبيقات الخاصة بنا من أي تطبيق عميل أو خدمة مختلفة.
ما سنعرضه هو كيفية إرسال طلب لإدراج جميع المستخدمين.
بمجرد فتح الأداة ، قمنا بتعيين عنوان URL https: // us-central1- {your-project} .cloudfunctions.net / api / users:
بعد ذلك ، في علامة التبويب ، نختار رمز Bearer وقمنا بتعيين القيمة التي استخرجناها من أدوات التطوير مسبقًا.
خاتمة
تهانينا! لقد انتهيت من البرنامج التعليمي بالكامل وتعلمت الآن إنشاء واجهة برمجة تطبيقات تستند إلى دور المستخدم على Firebase.
لقد قمنا أيضًا بتغطية كيفية استهلاكه من تطبيق Angular و Postman.
دعنا نلخص أهم الأشياء:
- يتيح لك Firebase الاستعداد والتشغيل بسرعة باستخدام واجهة برمجة تطبيقات مصادقة على مستوى المؤسسة ، والتي يمكنك توسيعها لاحقًا.
- يتطلب كل مشروع تقريبًا ترخيصًا - إذا كنت بحاجة إلى التحكم في الوصول باستخدام نموذج قائم على الدور ، تتيح لك مصادقة Firebase البدء بسرعة كبيرة.
- يعتمد النموذج القائم على الدور على التحقق من الموارد المطلوبة من المستخدمين ذوي الأدوار المحددة مقابل مستخدمين محددين.
- باستخدام تطبيق Express.js على Firebase Function ، يمكننا إنشاء واجهة برمجة تطبيقات REST وتعيين معالجات لمصادقة الطلبات وتفويضها.
- بالاستفادة من المطالبات المخصصة المضمنة ، يمكنك إنشاء واجهة برمجة تطبيقات مصادقة تستند إلى الأدوار وتأمين تطبيقك.
يمكنك قراءة المزيد حول مصادقة Firebase هنا. وإذا كنت تريد الاستفادة من الأدوار التي حددناها ، فيمكنك استخدام @ angular / fire المساعدين.