Почти всички приложения изискват от потребителите да се удостоверят, преди да позволят достъп до приложението и неговите функции.

Например, представете си сценарий, при който имате задачата да създадете приложение за ядрен реактор. Основното му изискване е да позволи на удостоверени и оторизирани потребители да щракнат върху бутон, който би взривил ядрена бомба някъде по света. Ако приложите системата, позволяваща на всеки потребител да вижда бутона за детониране, това ще причини значителен глобален пробив в сигурността и хаос навсякъде!

Ето защо, тази статия ще проучи как разработчиците могат да включат контрол на достъпа в своите React приложения, за да изградят по-сигурни компоненти и приложения ефективно.

Основите — Backend Authorization

Всеки разработчик трябва да гарантира, че всички заявки са разрешени в бекенда.

Но защо трябва да използвам упълномощаване на бекенда?

Разработчиците наистина могат да внедрят оторизация от страна на клиента и да наложат сигурност. Все пак е важно да се признае, че кодът на предния край винаги може да бъде модифициран с помощта на браузъра, което позволява на хакерите да заобиколят вашия код за оторизация на предния край и да експлоатират услугите на задния край.

Разработчиците могат да наложат бекенд оторизация по много начини. Например, за React приложения, разработчиците могат да създадат JWT токен при успешно удостоверяване и да предадат специфични твърдения, които можете да използвате по-късно, за да упълномощите потребителя.

exports.handler = async (event) => {
    // obtain jwt token

    const token = event.headers.Authorization.split(' ')[1];
   
    // verify jwt token

    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    // obtain custom "permission" claims

    const { permissions } = decoded; 
    
    // check if user has permission to access this function

    if (!permissions.includes('admin')) {
        return {
            statusCode: 401,
            body: JSON.stringify({
                error: 'Unauthorized'
            })
        };
    }
    const queryResp = await dynamodb.query({
        TableName: 'users',
        KeyConditionExpression: '#email = :email',
        ExpressionAttributeNames: {
            '#email': 'email'
        },
        ExpressionAttributeValues: {
            ':email': event.queryStringParameters.email
        }
    }).promise();
    return {
        statusCode: 200,
        body: JSON.stringify(queryResp.Items[0])
    }
};

Например, разгледайте кодовия фрагмент, показан по-горе. Той илюстрира проста функция AWS Lambda, която позволява на потребителската таблица да бъде запитвана, ако потребителят има разрешение „admin“. Това е един от многото начини, по които можете да приложите контрол на достъпа.

Например, можете да настроите прехващачи на заявки с междинен софтуер, за да разрешавате входящи заявки. Или пък, можете да настроите помощен метод, който разрешава входящата заявка, преди да сервира отговор. Като цяло, задължително е да имате оторизация на бекенда като логика за оторизация, тъй като предният край (JavaScript код) винаги може да бъде редактиран в браузъра и заобиколен.

Дефиниране на защитени React компоненти

След налагане на бекенд оторизация, разработчиците могат да започнат да проектират и внедряват защитени React компоненти. Има много начини да постигнете това. Затова нека разгледаме всеки метод един по един.

Предпоставки

Преди да внедрим защитени компоненти на React, връщането на списък с разрешения от бекенда към страната на клиента е от съществено значение. Разработчиците могат да извлекат тези разрешения и да ги съхраняват в LocalStorage, когато потребителят влезе успешно. По-късно тези разрешения могат да се използват за прилагане на детайлен контрол на достъпа за компоненти.

const doLogin = () => {
   const resp = await axios.post('/login', {
       username: 'username',
       password: 'password'
   });
   localStorage.setItem('token', resp.data.token);
   localStorage.setItem('permissions', JSON.stringify(resp.data.permissions));
}

Фрагментът по-горе илюстрира примерна заявка за удостоверяване, която вашето приложение React може да направи, за да получи токена за достъп JWT и разрешенията. Както е показано, разрешенията се запазват в LocalStorage за по-късна употреба.

Метод 01 — Условно изобразяване

Първо, може да има случаи, в които разработчиците може да искат да покажат само определен компонент при едно условие. В такива случаи може да се приеме подходът за условно изобразяване.

Например, ако потребителят е влязъл, може да искате да покажете компонента Reactor, но ако не, може да искате да покажете компонента Login. Как да използвате условно изобразяване за такива случаи е показано по-долу.

const App = () => {
  const isLoggedIn = localStorage.getItem('token');
  return (
    <Fragment>
      {isLoggedIn ? <Reactor /> : <Login />}
    </Fragment>
  );
}

Фрагментът по-горе показва компонента Reactor, изобразен, ако потребителят е влязъл (само ако токенът се съхранява в LocalStorage).

Метод 02 — Auth Guards

Понякога компонентите за условно изобразяване може да не са достатъчни. В такива ситуации може да искате да предотвратите достъпа на потребителите до определени маршрути във вашето приложение.

Например в нашето приложение за ядрен реактор може да не искате гражданите да имат достъп до маршрутите /detonate и /add-bomb. В случаите, когато искате да контролирате достъпа до конкретни маршрути на вашето приложение (вместо компоненти в рамките на маршрут), можете да обмислите използването на Auth Guard.

Можете да внедрите Auth Guard в React, като използвате концепцията за специализация на компоненти. Това е концепция на React, в която използвате базов компонент, за да разработите специализиран компонент. По този начин можете да използвате React Router, за да създадете базовия Route компонент и специализиран GuardedRoute компонент, който надгражда Route компонента.

Помислете за фрагмента, показан по-долу.

import React from "react";
import { Route, Redirect } from "react-router-dom";

const GuardedRoute = ({ component: Component, auth, ...rest }) => {
  <Route
    {...rest}
    render={(props) =>
      auth === true ? <Component {...props} /> : <Redirect to="/forbidden`" />
    }
  />;
};

export const Nav = () => {
  return <GuardedRoute 
    auth={true}component={() => <h1>Have Access To Nuclear Bomb Management</h1>}
    path={'/bomb-management'}
  />;
};

Фрагментът по-горе създава специализиран компонент на Route, наречен GuardedRoute. Той е отговорен за приемането на параметър за удостоверяване и изобразяването на компонента в маршрута само ако проверката за удостоверяване премине.

В нашия случай сме използвали охраната, за да дадем достъп до конзолата за управление на бомбата в ядрения реактор.

Метод 03 — Компоненти на обвивка

И накрая, може да има случаи, в които трябва да скриете или деактивирате конкретно съдържание на страница от категория потребители. В такива случаи условното изобразяване ще свърши работата. Това обаче прави вашия код сложен и по-малко четлив, което го прави по-труден за поддръжка.

Като илюстрация, помислете за ситуация, при която управителят на реактора и президентът имат достъп до маршрута /reactor-management в нашия ядрен реактор.

Президентът трябва да може да види секцията Детонация заедно с потребителите, упълномощени да управляват реактора. От друга страна, мениджърът на реактора не трябва да може да вижда тези компоненти.

В такива ситуации използването на условно изобразяване би довело до дублиране на код. Следователно можете да създадете компонент, който използва принципа на Containment на Reacts, където правите родителския компонент, като предавате деца в него. Това ви позволява да добавите логика за оторизация в родителския компонент и да изобразите дъщерния компонент само ако потребителят е упълномощен.

const Detenator = () => {
 return (
   <><button>Detonate Reactor</button></>
 );
};

const verifyPermissions = (permissions, userPermissions) => {
 let isVerified = true;
 if (!permissions || !userPermissions) isVerified = false;
 isVerified = permissions.every((permission) =>
   userPermissions.includes(permission)
 );
 return isVerified;
};

export const Authorizer = ({ permissions, requiredPermissions, children }) => {
 const isAuthorized = verifyPermissions(permissions, requiredPermissions);
 if (isAuthorized) {
   return children;
 }
 return (
   <><h1>Forbidden. No permissions to access</h1></>
 );
};
export const Page = ({ children }) => {
 return (
   <><h1>Nuclear Bomb Management</h1>
     {children}
     <Authorizer permissions={[]} requiredPermissions={["detonate", "president"]}>
       <Detenator /></Authorizer></>
 );
};

Фрагментът, показан по-горе, създава обвиващ компонент — Authorizer, който приема деца за изобразяване само ако потребителят има необходимите разрешения за достъп до компонента деца.

Съвпада с нашия случай на използване, тъй като Detenator може да бъде достъпен само ако потребителят има детонирани и президентски разрешения.

Заключителни мисли

Сигурността винаги е важна във всяко приложение. Сигурността винаги трябва да бъде ключов приоритет, дори ако изграждате приложение, толкова просто като приложение за задачи, или ако е толкова сложно, колкото ядрен детонатор.

Тази статия изследва три начина, по които разработчиците могат да проектират и внедрят защитени компоненти в React, за да помогнат за подобряване на сигурността на своите компоненти и сигурността на приложенията.

Кодът, внедрен в тази статия, е достъпен в моето хранилище на GitHub.

Надявам се, че сте намерили тази статия за полезна. Благодаря ви, че прочетохте.

Станете композируеми: Създавайте приложения по-бързо като Lego

Bit е инструмент с отворен код за изграждане на приложения по модулен и съвместен начин. Станете композируеми, за да изпращате по-бързо, по-последователно и лесно мащабируеми.

Научете повече

Създавайте приложения, страници, потребителски изживявания и потребителски интерфейси като самостоятелни компоненти. Използвайте ги, за да създавате по-бързо нови приложения и преживявания. Внесете всяка рамка и инструмент във вашия работен процес. Споделяйте, използвайте повторно и си сътрудничете, за да изграждате заедно.

Помогнете на вашия екип с:

Micro-Frontends

Системи за проектиране

Споделяне на код и повторно използване

Monorepos

Научете повече