Ai primit vreodată pachetul vecinului tău la ușa din față? (Poate chiar l-ai deschis din greșeală?) Poate ți-a rămas un mesaj vocal sensibil pentru altcineva? În calitate de dezvoltatori de aplicații, este datoria dumneavoastră să vă asigurați că datele sensibile stocate în aplicația dvs. nu sunt livrate accidental unei alte părți.
Există multe tehnici disponibile pentru a stoca în siguranță datele clienților și multe sunt extrem de complexe și dificil de implementat. În mod ideal, s-ar putea securiza toate datele clienților într-o singură bază de date - păstrând designul caracteristicii simplu și sigur.
Securitatea la nivel de rând (RLS) este capacitatea de a securiza și controla accesul la anumite rânduri de date din interiorul unui tabel al bazei de date. Este un instrument puternic care vă permite să stocați toate datele clienților dvs. într-o singură bază de date, fără nicio preocupare cu privire la scurgerile de date între conturi. Cu toate acestea, implementarea corectă a RLS poate fi un proces dificil care implică combinarea detaliilor de conectare cu permisiunile bazei de date. Neon Authorize simplifică acest proces integrând automat autentificarea de la furnizorul dvs. OAuth cu baza de date PostgreSQL.
Neon Authorize utilizează stratul de autentificare existent pentru a identifica fiecare utilizator conectat și asociază toate datele din baza de date cu acreditările lor de conectare. Acest lucru asigură că datele stocate în baza de date pot fi accesate numai de utilizatorii conectați și că numai utilizatorii conectați pot vedea datele lor.
Acest tutorial vă va ghida prin cum să creați o aplicație Remix folosind Clerk ca strat de autentificare. Clerk este un instrument popular de autentificare și gestionare a utilizatorilor. Veți folosi Neon Postgres ca strat de date și veți folosi Neon Authorize pentru a securiza toate datele pentru fiecare client conectat. Fiecare rând din tabel va desemna un ID de utilizator, care este furnizat de Clerk. Doar cei autentificați cu userID pot interacționa cu datele din rând.
Exemplul nostru de aplicație este simplu – înregistrează fiecare conectare în baza de date RLS, folosind ID-ul de utilizator. Când pagina este încărcată, vor fi afișate ultimele 10 autentificări pentru utilizatorul autentificat și nu vor apărea date ale altui utilizator (stocate în același tabel PostgreSQL). Să începem!
Începeți prin a crea o aplicație Remix și instalați dependențe folosind fragmentul de cod de mai jos. Pentru instrucțiuni mai detaliate, consultați ghidul de pornire rapidă Remix .
##make a directory and initialise your NPM project mkdir neon-authorize-remix-clerk-app cd neon-authorize-remix-clerk-app npm init -y ## install runtime dependecies for Remix npm i @remix-run/node @remix-run/react @remix-run/serve isbot@4 react react-dom @remix-run/router drizzle-orm npm install @neondatabase/serverless npm install @clerk/remix npm i -D @remix-run/dev vite
Deoarece Remix folosește Vite, un instrument de compilare Javascript, creați vite.config.js
în directorul rădăcină:
import { vitePlugin as remix } from "@remix-run/dev"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [remix()], });
Înainte de a face orice dezvoltare, trebuie să creăm conturi la Clerk și Neon pentru a le folosi serviciile:
Conectați-vă la tabloul de bord Clerk pentru a crea un proiect nou.
În navigarea din stânga, alegeți Chei API .
.env
în codul dvs.În navigarea din stânga, selectați „ Șabloane JWT. ”
Conectați-vă la consola Neon și creați un nou proiect.
Din meniul de navigare din stânga, selectați Autorizare .
Creați un furnizor nou și inserați adresa URL Clerk JWKS pe care ați copiat-o de la Clerk mai devreme.
După ce ați creat instanța, faceți clic pe „Începeți”. Se va deschide un panou lateral cu o serie de pași pentru a finaliza integrarea cu Neon Authorize.
Configurarea pentru început vă oferă pași pentru a configura un proiect de bază Autorizare cu Clerk.
1. Set up Neon Extension and Roles Privileges. Run these steps in the Dashboard. 2. Grant privileges to the roles in the neondb database.
Codul furnizat este pentru o aplicație todos. În loc să folosim codul standard furnizat de la Neon pentru o aplicație todos, vom crea un tabel login_history
și vom configura RLS pe acesta. Deschideți Editorul SQL în tabloul de bord Neon și rulați codul de mai jos. Tabelul login_history
va fi folosit pentru a stoca timpii de conectare pentru fiecare utilizator.
Rețineți că
login_history
are doar trei coloane: id, user_id și login_at. Ultimele două coloane vor afișa cele mai recente autentificări din aplicație.
CREATE TABLE login_history ( id bigint generated by default as identity primary key, user_id text not null default (auth.user_id()), login_at timestamp not null default now() ); -- 1st enable row level security for your table ALTER TABLE login_history ENABLE ROW LEVEL SECURITY; -- 2nd create policies for your table CREATE POLICY "Individuals can add login." ON login_history FOR INSERT TO authenticated WITH CHECK ((select auth.user_id()) = user_id); CREATE POLICY "Individuals can view their own logins. " ON login_history FOR SELECT TO authenticated USING ((select auth.user_id()) = user_id);
Adăugați variabilele de mediu furnizate la .env
Odată ce acești pași de configurare sunt finalizați, .env
ar trebui să aibă patru variabile: două de la Clerk și două de la Neon:
CLERK_PUBLISHABLE_KEY=pk_test_.... CLERK_SECRET_KEY=sk_test_... # Database owner connection string DATABASE_URL='postgresql://neondb_owner:...' # Neon "authenticated" role connection string DATABASE_AUTHENTICATED_URL='postgresql://authenticated@ep-...
Aplicația este acum gata să fie construită. Codul complet este disponibil pe GitHub , dar cele mai importante caracteristici sunt evidențiate aici. Nucleul aplicației este în app/routes/_index.tsx
:
export const loader: LoaderFunction = async (args) => { const { userId, getToken } = await getAuth(args); if (!userId) { return redirect("/sign-in"); } const authToken = await getToken(); console.log(userId); if (!authToken) { return null; } const DATABASE_AUTHENTICATED_URL= process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL; try { const sql = neon(DATABASE_AUTHENTICATED_URL ?? '', { authToken, }); const loginResponse = await sql(`INSERT INTO login_history ("user_id") VALUES ($1) RETURNING *`,[userId]); // Retrieve last 10 logins const last10LoginsResponse = await sql(`SELECT * FROM login_history WHERE user_id = $1 ORDER BY login_at DESC LIMIT 10`, [userId]); console.log(`loginResponse: ${JSON.stringify(loginResponse)}`); return last10LoginsResponse as Array<LoginHistory>; } catch (error) { console.error(`Error inserting into login_history table: ${error.message}`); console.error(`Error details: ${JSON.stringify(error)}`); throw error; } }
LoaderFunction
din fișierul _index.tsx
finalizează sarcini pe server înainte de a randa pagina pentru client. În această aplicație, încărcătorul face o mare parte din sarcinile grele ale aplicației.
Funcția verifică mai întâi dacă utilizatorul nu este autentificat și apoi redirecționează utilizatorul către pagina /sign-in
. Pagina de conectare poate fi configurată în tabloul de bord Clerk pentru a accepta diferite tipuri de conectare, cum ar fi autentificarea Google și e-mail:
Pentru a crea pagina de conectare, navigați la tabloul de bord Clerk și configurați metodele de conectare necesare pentru proiect.
Dacă utilizatorul este autentificat, funcția preia userId
și authToken
de la Clerk. Aceste valori sunt esențiale pentru a vă asigura că utilizatorul este conectat și apoi puteți utiliza userId
pentru a popula fiecare rând din baza de date.
Pentru a face modificări în baza de date securizată prin RLS, trebuie să extrageți DATABASE_AUTHENTCATED_URL
din variabilele de mediu.
Logica de bază pentru implementarea securității RLS se află în funcția LoaderFunction
. O instanță SQL Neon este inițializată folosind variabilele de mediu și simbolul de autentificare. Funcția loginResponse
efectuează un apel SQL și inserează user_id (și ora curentă) în baza de date PostgreSQL, după care funcția last10LoginsResponse
interogă DB pentru cele mai recente 10 autentificări.
În cele din urmă, last10LoginsResponse
este returnat de la funcția de încărcare.
Funcția Index()
din fișierul _index.tsx
redă aspectul paginii așa cum se arată în fragmentul de mai jos:
export default function Index() { const logins = useLoaderData(); return ( <div> <h1>Signed in</h1> <p>You are signed in!</p> <p> <UserButton /></p> <div> <h1>Recent Logins</h1> {logins?.map((logins) => ( <li key={logins.id}> {logins.user_id} login at: {logins.login_at} </li> ))} </div> <p>< SignOutButton > Sign Out</ SignOutButton ></p> </div> ); }
Codul de mai sus preia răspunsul de la LoaderFunction
, care conține ultimele 10 intrări de conectare. Acest răspuns creează o pagină care îi spune utilizatorului că este conectat, listează ultimele 10 autentificări și afișează un buton Deconectare, așa cum se arată mai jos:
În acest exemplu, user_id
este, de asemenea, afișat pentru a indica clar că numai datele de conectare pentru utilizatorul conectat sunt vizibile.
Folosind o fereastră incognito, vă puteți conecta cu un al doilea cont Google și puteți vizualiza date una lângă alta pentru diferiți utilizatori:
Rețineți că timpii de conectare se suprapun, dar prin utilizarea Securității la nivel de rând în baza de date, veți preveni scurgerea datelor între conturi. Rândurile pot fi extrase și afișate numai pentru utilizatorul autentificat.
Păstrarea datelor private este un caz de utilizare critic. Deoarece aplicațiile stochează adesea informații private, acestea trebuie să fie securizate pentru a păstra datele în mâinile potrivite. Consumatorii au din ce în ce mai multe protecții legale, cum ar fi GDPR, iar instrumente precum Neon Authorize facilitează implementarea Row Level Security pentru a vă proteja datele clienților.
În această postare, am parcurs pașii necesari pentru a activa securitatea la nivel de rând într-o bază de date Neon. Utilizarea RLS cu datele clienților noștri asigură că numai utilizatorul conectat are acreditările pentru a-și extrage propriile date.
Adăugați Row Layer Security la aplicația dvs. astăzi cu Neon.