de Vitaly Kondratiev

De ce nu ar trebui (aproape) să mai folosiți niciodată o cale absolută către API-urile dvs.

De ce nu ar trebui aproape sa mai folositi o
bărbat purtând tricou gri în picioare pe pădure de Caleb Jones pe Unsplash.

Progresele recente în arhitectura aplicațiilor web arată că un front-end decuplat oferă mai multă flexibilitate pentru dezvoltare și operațiuni. Aceasta

  • vă permite să lucrați la un capăt fără a depinde de celălalt
  • vă permite să construiți și să implementați separat
  • face mai ușor să folosiți diferite instrumente la fiecare capăt
1611491169 806 De ce nu ar trebui aproape sa mai folositi o
Arhitectură front-end decuplată

Problema

Când o aplicație este dezvoltată având în vedere această arhitectură, front-end-ul trebuie să comunice cu back-end-ul prin API-uri, în general ODIHNĂ. Adesea, adresa URL / portul serverului back-end diferă de cel al front-end-ului, având în vedere căi de implementare separate. În acest exemplu, adresa URL a aplicației front-end este https://www.appfocused.com iar punctul final REST pentru a trimite e-mailuri de contact este servit de la https://api.appfocused.com.

O solicitare HTTP de la aplicația front-end către serverul back-end va eșua, deoarece încalcă Politica privind aceeași origine. În consola Chrome va arăta astfel:

1611491169 769 De ce nu ar trebui aproape sa mai folositi o
Eroare CORS

Din motive de securitate, browserele restricționează cererile care nu provin din aceeași origine. Acest lucru împiedică atacatorii să injecteze cod în aplicația noastră și să ne fure informațiile sensibile. Browserele adaugă un origin antet pe cereri de origine încrucișată pentru a anunța serverul despre o potențială amenințare. Serverul are apoi autoritatea fie să permită, fie să respingă aceste origini, oferind anteturi de răspuns specifice, care sunt analizate de browsere.

Există două soluții pentru a remedia această mică problemă:

  • adresele URL absolute ale codului dur pe client și configurați antetele CORS pe server
  • utilizați adresele URL relative ale clientului și utilizați o abordare proxy inversă

În această postare, vorbesc despre de ce abordarea anterioară cu anteturile CORS ar trebui considerată un anti-model pentru codul pregătit pentru producție. Vorbesc, de asemenea, despre cum să configurați abordarea proxy invers pe diverse configurări:

  • devserver local
  • server web / server de aplicații
  • fără server (CloudFront / S3 / Lambda)

Justificare

Scenariul antetelor CORS pare a fi mai puțin dureros de implementat și este. Cu toate acestea, există câteva îngrijorări de luat în considerare care m-au determinat să predic pentru abordare proxy inversă în aproape orice împrejurări.

În primul rând, este posibil ca back-end-ul să nu fie deținut de dvs. și ar putea fi imposibil să faceți schimbarea la antetele CORS.

Dacă aveți norocul să controlați back-end-ul și poate configura anteturile CORS, va trebui să mențineți o listă albă de mai mulți clienți web care accesează serverul API pentru a le oferi acces. Desigur, wildcard-ul este, de asemenea, o opțiune, dar nu ar fi nerezonabil să listați toate originile în alb pe setări access-control-allow-origin la * dacă nu este un server public.

Un alt model obișnuit, în timpul dezvoltării, este să rulăm aplicația UI la localhost:$port. Dar lista albă localhost pentru a facilita apelurile API este un anti-model și ar trebui evitată din motive de securitate.

Nu în ultimul rând, îmi place construcția mea să se conformeze cu Construiește o dată, implementează multe principiu. Este unul dintre principiile fundamentale ale livrare continua. Binarul – în cazul nostru, fișiere statice pentru clientul web – este construit o singură dată. Implementările, testările și lansările ulterioare nu ar trebui să încerce niciodată să construiască din nou artefacte binare. În schimb, binarul deja construit ar trebui reutilizat.

În practică, adresele URL absolute codificate hard, cum ar fi https://api.appfocused.com/email/send în codul nostru de client ne va împiedica să avem un singur artefact, deoarece în mediul de dezvoltare vreau ca clientul meu web să lovească, să zicem, https://api-dev.appfocused.com/email/send.

Nu codificați niciodată o adresă URL absolută API în codul dvs. de client.

Aceasta a devenit o mantra puternică pentru mine și m-a ajutat să depășesc unele provocări pe drum.

Soluţie

Adresa URL relativă /email/send o poate rezolva o dată pentru totdeauna pe client, făcând posibilă Construirea O dată, Implementarea Multe. Este sarcina proxy-ului să orchestreze cererea în continuare. De asemenea, se ocupă de restricțiile impuse de browser. Serverul proxy, în acest caz, gestionează solicitările, răspunsurile și face modificările necesare pentru a facilita comunicarea cross-origine.

Reverse proxy cu webpack-dev-server

Când vă dezvoltați pe mașina dvs. locală, doriți același tratament pentru API-ul dvs. ca și în alte medii. Webpack poate fi configurat pentru solicitări proxy. Un exemplu „webpack.config.js” este:

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:9000'
    }
  }
};

O cerere din partea clientului către calea relativă /api/users va transmite acum cererea către http://localhost:9000/api/users. Vă rugăm să verificați Wdocumentație ebpack dacă doriți să configurați scenarii de rescriere URL sau să adăugați protocol securizat.

Proxy-ul poate fi configurat și pentru proiecte construite pe Webpack, cum ar fi creați-reacționați-aplicație sau Gatsby.

Reverse proxy cu NGINX

NGINX este o componentă comună în arhitectura mediului de producție. Are o serie de funcții avansate de echilibrare a sarcinii, securitate și accelerație de care lipsesc majoritatea aplicațiilor specializate. Utilizarea NGINX ca un proxy invers vă permite să adăugați aceste caracteristici la orice aplicație.

Cea mai simplă configurare a proxy-ului invers pe NGINX va arăta astfel, în „/etc/nginx/conf.d/app.conf”

server {
  listen 80;
  listen [::]:80;
  
  server_name appfocused.com;
  
  location /api {
      proxy_pass http://api.appfocused.com/;
  }
}

proxy_pass directiva face din această configurație un proxy invers. Specifică faptul că toate cererile care se potrivesc cu blocul de locație – în acest caz, /api cale – ar trebui să fie redirecționată către http://api.appfocused.com, unde rulează back-end-ul nostru.

Verifică documente complete pentru unele scenarii mai elaborate.

Proxy invers cu serverless

Ne vom uita la platforma AWS pentru un scenariu fără server. Într-una din postările mele anterioare am explicat cum folosim arhitectură fără server pentru a găzdui site-ul nostru web. AWS CloudFront joacă unul dintre rolurile cheie în acesta, acționând ca CDN și oferind securitate la marginea fișierelor noastre statice stocate pe S3.

Primul API pe care a trebuit să îl integrăm în această configurare a fost un formular de contact. Rezumatul pentru implementare a fost următorul:

Când un client postează în https://www.appfocused.com/api/send/email, cererea trebuie direcționată către https://api.appfocused.com/api/send/email unde API-ul nostru back-end este implementat sub forma funcției Lambda.

Se pare că CloudFront acceptă mai multe servere de origine. Folosește modele de cale pentru a determina către ce server de origine să redirecționeze cererile. Mai multe servere independente, chiar și sisteme care nu se află în AWS, pot „deține” una sau mai multe căi sub un singur nume de gazdă. Una dintre ele este implicită și deține toate căile care nu sunt configurate în mod explicit.

Conceptul este foarte asemănător cu proxy-urile inversate din NGINX sau Apache. Dar rutarea cererii este realizată de CloudFront, care se conectează la back-end-ul corespunzător, trimite cererea și returnează – și, eventual, cache – răspunsul. Nu redirecționează solicitarea, astfel încât adresa URL nu se schimbă niciodată pentru consumator.

Exemplu de configurare CloudFront

De exemplu, utilizați numele gazdei site-ului principal www.appfocused.com, ca origine. Configurați numele domeniului site-ului ca un nume de domeniu alternativ în CloudFront.

Apoi, adăugați o a doua origine, cu destinația fiind numele de gazdă unde poate fi atinsă implementarea WP. Creați un comportament cu un tipar de cale care se potrivește /blog* și folosește a doua origine.

Distribuția noastră CloudFront existentă a fost configurată pentru a indica conținutul nostru static S3 generat de marele Gatsby. Tine minte nu să utilizați autosugestia de la AWS atunci când creați o nouă distribuție cu integrare în S3. Intră manual puncte finale ale site-ului web similar cu acest format http://appfocused.s3-website.eu-west-1.amazonaws.com.

Apoi, vom adăuga a doua noastră origine pentru a servi cererile REST de la API Gateway. Din fila „Origini” selectați „Creați origine”. Introduceți numele domeniului și lăsați calea de origine goală. Asigurați-vă că selectați „Numai HTTPS” pentru „Politica privind protocolul de origine”.

1611491169 184 De ce nu ar trebui aproape sa mai folositi o
Cloudfront: creați originea

Apoi accesați fila „Comportamente” și faceți clic pe „Creați comportament” pentru a configura calea.

1611491169 12 De ce nu ar trebui aproape sa mai folositi o
Cloudfront: creați un comportament

Pentru „Pattern Pattern” vom folosi api/* . Aceasta va prinde orice cerere începând cu /api precum https://www.appfocused.com/api/send/email.

În meniul derulant „Origine”, selectați Originea pe care tocmai am creat-o. Acest lucru va asigura că solicitarea va fi direcționată către https://api.appfocused.com/api/send/email.

Pentru „Politica privind protocolul vizualizatorului”, selectați „Numai HTTPS”.

Pentru „Metode HTTP permise” selectați „GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE”.

Pentru „Memorie cache pe baza antetelor de solicitare selectate”, selectați „Lista albă” și adăugați anteturile necesare. Acest lucru împiedică trecerea antetului gazdă la origine.

Pentru „Caching de obiecte” selectați „Utilizați anteturile cache-ului de origine”.

Pentru „Redirecționați cookie-uri” selectați „Toate”.

Pentru „Comprimarea automată a obiectelor” selectați „Da”. Acest lucru va gzip răspunsuri.

CloudFront transmite în mod implicit foarte puține anteturi către origine. Puteți să-l configurați pentru a redirecționa ceea ce aveți nevoie, dar fiecare antet pe care îl redirecționați o va face reduce raportul dvs. de accesări cache. Personal trec prin „Referer”, „Accept”, „Content-Type” și „Authorization”.

Cu toate acestea, există câteva avertismente pentru proxy-ul fără server pe AWS. CloudFront nu va elimina căile.

Dacă o cerere este trimisă la https://www.appfocused.com/api/* va fi direcționat către https://api.appfocused.comcu /api prefix, nu la rădăcina site-ului.

Aceasta poate deveni o problemă dacă nu dețineți API-uri back-end sau, din anumite motive, acestea nu pot fi modificate. Dacă așa stau lucrurile, Lambda @ Edge vine în ajutor. Acest serviciu vă permite să rescrieți căi din mers, pe măsură ce solicitările sunt procesate. Pentru a configura Lambda @ Edge, accesați elementul Comportament CloudFront și alegeți „Asociații de funcții Lambda”.

Concluzie

Prin implementarea unui proxy invers în medii, realizăm:

  • Comunicare securizată client-server
    identitatea serverelor dvs. back-end rămâne necunoscută. Acest lucru este util în cazul atacurilor DDoS
  • Construiește o dată, implementează multe
    cu căi relative la API-uri, puteți construi o singură dată și implementa același artefact în mai multe medii
  • Aceeași origine
    nu este necesară configurarea antetelor CORS pe server

Sfatul meu personal este: să nu mai codificați niciodată căile absolute către API-urile dvs., cu excepția cazului în care este un prototip. Petreceți puțin mai mult timp pentru a configura un strat proxy invers pentru a-l face corect.

Această postare a fost publicată inițial pe blogul companiei mele. Misiunea noastră la Appfocused este de a ajuta companiile să execute experiențe excelente pentru utilizatori pe web utilizând vasta noastră experiență, cunoștințe despre tendințele moderne ale interfeței de utilizare, cele mai bune practici și artizanatul codului.