احراز هویت در برنامه‌های React

ترجمه و تالیف : مهدی عقیقی
تاریخ انتشار : 20 فروردین 99
خواندن در 2 دقیقه
دسته بندی ها : react

چگونه با استفاده از هوک‌ها و کانتکست، در برنامه‌های React احراز‌هویت کنیم؟

پرش به پایان

راز کلی این مقاله را می‌توانید در مثال زیر ببینید.

import React from 'react'
2import {useUser} from './context/auth'
3import AuthenticatedApp from './authenticated-app'
4import UnauthenticatedApp from './unauthenticated-app'
5
6function App() {
7  const user = useUser()
8  return user ? <AuthenticatedApp /> : <UnauthenticatedApp />
9}
10
11export App

همین. بیش‌تر برنامه‌هایی که نیاز به احراز هویت دارند، می‌توانند به جای این که کاربران را هنگام مراجعه به صفحات نیازمند احراز هویت، ریدایرکت کنند، از روش بالا برای راحت‌تر کردن کار خود استفاده کنند.  و این کار باعث می‌شود صفحاتی که نیازمند احراز هویت است، کلا رندر نشوند. شما می‌توانید برای بهتر شدن روش بالا حتی از فن زیر استفاده کنید:

import React from 'react'
import {useUser} from './context/auth'
const AuthenticatedApp = React.lazy(() => import('./authenticated-app'))
const UnauthenticatedApp = React.lazy(() => import('./unauthenticated-app'))
function App() {
  const user = useUser()
  return user ? <AuthenticatedApp /> : <UnauthenticatedApp />
}
export App

عالی. هم‌اکنون شما هیچ کدی را تا زماتی که به آن نیاز پیدا نکرده‌اید لود نمی‌کنید. پس صفحه‌ی لاگین برای افرادی که احراز هویت نشده‌اند و صفحه‌های دیگر برای بقیه افراد سریع‌تر لود می‌شود.

این که <AuthenticatedApp /> یا <UnauthenticatedApp /> چه می‌کنند کاملا مربوط به خود شماست. حتی شاید از دو Router جدا استفاده کنند. یا حتی از کامپوننت یکسان استفاده کنند. اما مهم این است که دیگر شما خودتان را برای این که چک کنید اگر یوزر لاگین نیست، آن را ریدایرکت کنید، اذیت نمی‌کنید.

خب حالا چه کار کنیم تا به این نقطه برسیم؟ بیاید به آنجایی که App را رندر می‌کنیم نگاهی بی‌اندازیم.

import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
import AppProviders from './context'
ReactDOM.render(
  <AppProviders>
    <App />
  </AppProviders>,
  document.getElementById('root'),
)

و AppProviders:

import React from 'react'
import {AuthProvider} from './auth-context'
import {UserProvider} from './user-context'
function AppProviders({children}) {
  return (
    <AuthProvider>
      <UserProvider>{children}</UserProvider>
    </AuthProvider>
  )
}
export default AppProviders

خب هم‌اکنون ما یک Provider برای احراز هویت و یک Provider برای اطلاعات User داریم. پس ما می‌توانیم از AuthProvider برای راه‌اندازی اولیه اطلاعات استفاده کنیم. (در صورتی که رمز در localStorage موجود باشد، می‌توانیم به راحتی این اطلاعات را از طریق رمز دریافت کنیم.) و سپس UserProvider می‌تواند برای نگه‌داشتن اطاعات در مموری و سرور، و همچنین تغییرات اطلاعات استفاده شود. (مانند تغییر ایمیل، آدرس و ...).

فایل auth-context.js حاوی چیز‌هایی است که کمی خارج از موضوع این مقاله می باشد، پس من فقط یک نسخه کوچک شده از آن‌را می‌گذارم که شما استفاده کنید:

import React from 'react'
import {FullPageSpinner} from '../components/lib'
const AuthContext = React.createContext()
function AuthProvider(props) {
  // code for pre-loading the user's information if we have their token in
  // localStorage goes here
  // ? this is the important bit.
  // Normally your provider components render the context provider with a value.
  // But we post-pone rendering any of the children until after we've determined
  // whether or not we have a user token and if we do, then we render a spinner
  // while we go retrieve that user's information.
  if (weAreStillWaitingToGetTheUserData) {
    return <FullPageSpinner />
  }
  const login = () => {} // make a login request
  const register = () => {} // register the user
  const logout = () => {} // clear the token in localStorage and the user data
  // note, I'm not bothering to optimize this `value` with React.useMemo here
  // because this is the top-most component rendered in our app and it will very
  // rarely re-render/cause a performance problem.
  return (
    <AuthContext.Provider value={{data, login, logout, register}} {...props} />
  )
}
const useAuth = () => React.useContext(AuthContext)
export {AuthProvider, useAuth}
// the UserProvider in user-context.js is basically:
// const UserProvider = props => (
//   <UserContext.Provider value={useAuth().data.user} {...props} />
// )
// and the useUser hook is basically this:
// const useUser = () => React.useContext(UserContext)

ایده‌ی اصلی که باعث می‌شود این روش را یکی از بهترین روش‌های احراز هویت در ریکت بکند این است که:

کامپوننتی که شامل اطلاعات کاربر می‌باشد، از رندر شدن بقیه کامپوننت‌ها که تا زمان گرفتن اطلاعات یوزر لازم نیستند، جلوگیری می‌کند.

و این کار را با نشان دادن یک spinner لودینگ به جای بقیه app انجام می‌دهد. این روش حتی Router یا هر چیز دیگری را هم رندر نمی‌کند، تا زمانی که متوجه بشود یک رمز در localStorage موجود است و با آن رمز تلاش برا گرفتن اطلاعات از سرور را بکند.

بعد از این که دریافت اطلاعات یوزر از سرور تمام شد، برنامه بقیه کامپوننت‌ها را رندر می‌کند.

سخن پایانی

برنامه‌های مختلف در پیاده کردن authentication متفاوت هستند. اگر شما از Server-side Rendering استفاده می‌کنید، احتمالا شما نیاز به یک spinner نخواهید داشت و اطلاعات یوزر را همان زمانی که شروع به رندر کردن می‌کنید، دارید.

اگر می‌خواهید با یک نسخه ساده شده از این روش کمی بازی کنید، از سندباکس زیر استفاده کنید.

سندباکس

امیدوارم که این مقاله به شما کمک کرده باشد.

پانوشت

چند تا از دوستان از من پرسیدند: اگر برنامه‌ی ما صفحه‌های زیادی که برای هر دو کاربران احراز هویت شده و نشده داشته باشید، (مثل Twitter) چه کار کنیم؟

در این مورد، شما احتمالا باید از تعداد زیادی useUser() در کد خود استفاده کنید.، یا حتی می‌توانید با ساختن یک هوک به اسم useIsAuthenticated() که یک Boolean برگرداند، کار خود را راحت‌تر از این‌ها بکنید. خوشبختانه با استفاده از Context و Hook انجام این کار خیلی راحت‌تر شده است.

همچنین میتوانید از دوره‌ی ریکت موجود در وبسایت راکت استفاده کنید.

منبع

گردآوری و تالیف مهدی عقیقی

برنامه‌نویس وب، عاشق جاوااسکریپت و ریکت و لاراول :)