Ви коли-небудь отримували пакунок від свого сусіда біля вхідних дверей? (Можливо, навіть випадково відкрив його?) Можливо, у вас залишилося конфіденційне голосове повідомлення для когось іншого? Як розробники програм, ваша робота полягає в тому, щоб конфіденційні дані, що зберігаються у вашій програмі, не були випадково доставлені іншій стороні.
Існує багато методів, доступних для безпечного зберігання даних клієнтів, і багато з них дуже складні та важкі для впровадження. В ідеалі можна було б захистити всі дані клієнта в одній базі даних, зберігаючи дизайн функції простим і безпечним.
Безпека на рівні рядків (RLS) — це можливість захищати та контролювати доступ до певних рядків даних у таблиці бази даних. Це потужний інструмент, який дозволяє зберігати всі ваші клієнтські дані в одній базі даних, не турбуючись про витік даних між обліковими записами. Однак правильна реалізація RLS може бути складним процесом, пов’язаним із поєднанням даних для входу та дозволів бази даних. Neon Authorize спрощує цей процес, автоматично інтегруючи автентифікацію від вашого постачальника OAuth у вашу базу даних PostgreSQL.
Neon Authorize використовує ваш існуючий рівень автентифікації для ідентифікації кожного користувача, який увійшов у систему, і пов’язує всі дані у вашій базі даних з їхніми обліковими даними для входу. Це гарантує, що дані, які зберігаються в базі даних, будуть доступні лише користувачам, які ввійшли в систему, і що лише користувачі, які ввійшли в систему, можуть бачити свої дані.
У цьому посібнику ви дізнаєтеся, як створити програму Remix, використовуючи Clerk як рівень автентифікації. Clerk — популярний інструмент аутентифікації та керування користувачами. Ви використовуватимете Neon Postgres як рівень даних і використовуватимете Neon Authorize, щоб захистити всі дані кожного клієнта, який увійшов у систему. Кожен рядок у таблиці позначатиме ідентифікатор користувача, який надає Клерк. Тільки ті, хто пройшов автентифікацію за допомогою ідентифікатора користувача, можуть взаємодіяти з даними в рядку.
Наш зразок програми простий — він записує кожен вхід до бази даних RLS за допомогою ідентифікатора користувача. Коли сторінка завантажується, відображатимуться останні 10 логінів автентифікованого користувача, а дані інших користувачів (збережені в тій самій таблиці PostgreSQL) не відображатимуться. Давайте почнемо!
Почніть зі створення програми Remix і встановлення залежностей за допомогою фрагмента коду нижче. Щоб отримати докладніші інструкції, зверніться до короткого посібника користувача 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
Оскільки Remix використовує Vite, інструмент збірки Javascript, створіть vite.config.js
у кореневому каталозі:
import { vitePlugin as remix } from "@remix-run/dev"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [remix()], });
Перш ніж розпочинати будь-які розробки, нам потрібно створити облікові записи в Clerk і Neon, щоб користуватися їхніми послугами:
Увійдіть на інформаційну панель Clerk, щоб створити новий проект.
На панелі навігації ліворуч виберіть Ключі API .
.env
у своєму коді.У лівій панелі навігації виберіть « Шаблони JWT. »
Увійдіть у консоль Neon і створіть новий проект.
У навігаційному меню ліворуч виберіть Авторизувати .
Створіть нового постачальника та вставте URL-адресу Clerk JWKS , яку ви скопіювали з Clerk раніше.
Створивши екземпляр, натисніть «Почати». Відкриється бічна панель із низкою кроків для завершення інтеграції Neon Authorize.
Початкове налаштування містить кроки для налаштування базового проекту авторизації за допомогою 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.
Наданий код призначено для програми todos. Замість використання наданого шаблонного коду від Neon для програми todos ми створимо таблицю login_history
і налаштуємо RLS на ній. Відкрийте редактор SQL на інформаційній панелі Neon і запустіть наведений нижче код. Таблиця login_history
буде використовуватися для зберігання часу входу для кожного користувача.
Зверніть увагу, що
login_history
має лише три стовпці: id, user_id і login_at. Останні два стовпці відображатимуть останні входи в програму.
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);
Додайте надані змінні середовища до свого .env
Після завершення цих кроків налаштування ваш .env
має мати чотири змінні: дві від Clerk і дві від 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-...
Тепер програма готова до створення. Повний код доступний на GitHub , але найважливіші функції виділено тут. Ядро програми знаходиться в 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
у файлі _index.tsx
виконує завдання на сервері перед відтворенням сторінки для клієнта. У цій програмі завантажувач виконує більшу частину важкої роботи програми.
Функція спочатку перевіряє, чи не ввійшов користувач, а потім перенаправляє користувача на сторінку /sign-in
. Сторінку входу можна налаштувати на інформаційній панелі Clerk для прийняття різних типів входу, як-от Google та електронної пошти:
Щоб створити сторінку входу, перейдіть до інформаційної панелі Clerk і налаштуйте необхідні методи входу для проекту.
Якщо користувач увійшов у систему, функція отримує userId
і authToken
від Clerk. Ці значення необхідні для того, щоб переконатися, що користувач увійшов у систему, а потім ви можете використовувати userId
для заповнення кожного рядка у вашій базі даних.
Щоб внести зміни в базу даних, захищену RLS, потрібно отримати DATABASE_AUTHENTCATED_URL
зі змінних середовища.
Основна логіка реалізації безпеки RLS лежить у LoaderFunction
. Екземпляр SQL Neon ініціалізується за допомогою змінних середовища та маркера автентифікації. Функція loginResponse
здійснює виклик SQL і вставляє user_id (і поточний час) у базу даних PostgreSQL, після чого функція last10LoginsResponse
запитує в БД 10 останніх входів.
Нарешті, last10LoginsResponse
повертається функцією завантажувача.
Функція Index()
у файлі _index.tsx
відображає макет сторінки, як показано у фрагменті нижче:
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> ); }
Наведений вище код отримує відповідь із функції LoaderFunction
, яка містить останні 10 записів для входу. Ця відповідь створює сторінку, яка повідомляє користувачеві, що він увійшов, перераховує його останні 10 логінів і показує кнопку «Вийти», як показано нижче:
У цьому прикладі user_id
також відображається, щоб чітко вказати, що видимі лише дані для входу користувача, який увійшов у систему.
Використовуючи вікно анонімного перегляду, ви можете увійти за допомогою другого облікового запису Google і паралельно переглядати дані для різних користувачів:
Зауважте, що час входу збігається, але використовуючи захист на рівні рядків у базі даних, ви запобіжите витоку даних між обліковими записами. Рядки можуть бути витягнуті та відображені лише для автентифікованого користувача.
Зберігання конфіденційності даних є критично важливим випадком використання. Оскільки програми часто зберігають конфіденційну інформацію, її потрібно захищати, щоб дані залишалися в правильних руках. Споживачі отримують дедалі більше правового захисту, як-от GDPR, а такі інструменти, як Neon Authorize, спрощують впровадження захисту на рівні рядків для захисту даних клієнтів.
У цій публікації ми розглянули кроки, необхідні для ввімкнення безпеки на рівні рядків у базі даних Neon. Використання RLS із даними нашого клієнта гарантує, що лише користувач, який увійшов у систему, матиме облікові дані для отримання власних даних.
Додайте Row Layer Security у свою програму вже сьогодні за допомогою Neon.