Webbarkitekturens utveckling: PHP-nostalgi och CORS-mardrömmen

📖 12min read

”På den tiden räckte det med en enda fil…”

PHP som jag arbetade med under studietiden var verkligen bekvämt. I en enda index.php-fil kunde man lägga in HTML-taggar, öppna <?php ?> mitt i och även skriva kod för databasfrågor där. Det fanns ingen tydlig gräns mellan frontend och backend. När man hade deklarerat en variabel kunde man använda den var som helst i HTML-koden, och fel på grund av datakommunikation var nästan aldrig ett problem.

Men när jag i arbetslivet gick över till en stack med Vue.js i frontend och Spring Boot i backend började helvetet.

”Jag har ju startat Spring-servern lokalt, Vue-skärmen är också uppe, så varför kommer inte datan fram?”

Konsolen i Chrome DevTools var täckt av knallröda felmeddelanden.

Access to XMLHttpRequest at ... has been blocked by CORS policy

Vad är egentligen det här ”CORS” som stoppar min data? Och varför övergav vi den bekväma gamla metoden där en enda fil räckte, bara för att medvetet dela upp frontend och backend?

Förr bodde de under samma tak. Nu bor de i olika hus och måste passera en kontroll.

Vem ska laga maten? (SSR vs CSR)

För att förstå den här förändringen måste man först förstå vem som lagar ”rätten” som kallas webbsida, alltså HTML. Det är precis där skillnaden mellan SSR (Server Side Rendering) och CSR (Client Side Rendering) ligger.

1. SSR: en färdig lunchlåda (PHP, JSP)

2. CSR: en hemlevererad meal kit (React, Vue)

[Tip] CSR och SPA är inte exakt samma sak

Begreppen används ofta som om de vore samma sak, men strikt sett är de inte det.

Vanligtvis använder vi CSR för att bygga en SPA. Med andra ord: eftersom vi vill undvika blinkningar från full sidomladdning genom att använda en enda sida (SPA), väljer vi ett upplägg där webbläsaren själv ritar om de delar som behövs (CSR).

Skälet till att vi använder Vue.js eller React är användarupplevelsen. För att en webbplats ska kännas lika smidig som en mobilapp flyttade vi själva ”matlagningen” från servern till webbläsaren.

CORS, vakten som stoppar objudna gäster

När frontend (Vue) och backend (Spring) delas upp får vi i praktiken ”två hus”.

Det är här problemet börjar. Av säkerhetsskäl följer webbläsaren en grundregel: ”lita inte lättvindigt på resurser som kommer från en annan Origin.” Det är SOP, alltså Same Origin Policy.

Tänk dig det här. Jag är inloggad på Naver, och från en webbplats skapad av en hacker skickas i smyg en begäran till Naver-servern som säger: ”ge mig användarens personuppgifter.” Om webbläsaren inte stoppade det, skulle all min information ligga helt öppen.

Därför blockerar webbläsaren som standard begäranden när origin, alltså domän eller port, är annorlunda. Men för utvecklingens skull har vi medvetet delat upp portarna. Vi vill bara använda vår egen data, och ändå säger webbläsaren, som en vakt: ”Din port stämmer inte. Tillträde förbjudet.” Det är den verkliga innebörden av ett CORS-fel, alltså Cross-Origin Resource Sharing.

[Code Verification] Utfärda ett passerkort

Lösningen på det här problemet är egentligen enkel. Servern, alltså backend, behöver bara skriva ett passerkort till webbläsaren, vakten, som säger: ”Den här personen är en gäst som jag själv har bjudit in. Släpp in honom.”

I Spring Boot kan det här passerkortet utfärdas med en enda konfigurationsfil.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 1. For alla API-vagar
                .allowedOrigins("http://localhost:8080") // 2. Tillat forfragningar fran denna adress
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 3. Tillat dessa typer av forfragningar
                .allowCredentials(true); // 4. Cookies och autentiseringsuppgifter tillats
    }
}

Analys:

Det viktiga är att man inte av bekvämlighet öppnar allt med allowedOrigins("*"). Det vore som att lämna ytterdörren vidöppen med en skylt där det står: ”Välkomna, tjuvar.”

Praktiskt råd: när ska man använda vad?

Betyder det då att CSR, alltså Vue eller React, alltid är det rätta svaret? Nej. I praktiken måste valet göras utifrån syftet.

Avslutning: att ta sig över gränsen

Den gamla ”stor familj under samma tak”-modellen från PHP-tiden var bekväm, men ju mer allt växte i komplexitet, desto svårare blev det att hantera. Dagens modell med ”två separerade familjer”, frontend och backend, är mer besvärlig på grund av kommunikation och CORS, men den gör det också möjligt för varje sida att fokusera på sin egen roll.

Nästa gång du stöter på ett CORS-fel, försök därför att inte bara bli irriterad. Webbläsaren försöker inte jävlas med dig. Den genomför bara en noggrann kontroll för att skydda ditt hus, alltså din server.

Nu är vägen mellan frontend och backend öppen. Datan kan färdas fram och tillbaka. Men var lagras den egentligen på serversidan? Förr kunde jag bara skicka iväg en fråga och hämta det jag behövde. I Spring känns det däremot, på grund av saker som JPA och Entity, nästan som om man inte längre får skriva SQL direkt.

Nästa gång ska vi titta på hur den ”skolboksmässiga” modellen för databasdesign, normalisering, i praktiken kan bli ett hinder, och varför JPA så gärna försöker dölja SQL för oss.

Lämna en kommentar