1661335260
Узнайте, как реализовать функцию сброса пароля с помощью Axios, useRouter Hook и динамических маршрутов Next.js, отдавая приоритет UX.
Большинство пользователей, как правило, забывают свои пароли, поэтому создавайте возможности, которые всегда будут ценны для людей, использующих ваши продукты. В этой статье мы обсудим, как реализовать функцию сброса пароля с динамическими маршрутами Next.js, отдавая приоритет UX.
В общем смысле аутентификация представляет собой препятствие для многих людей, когда они пытаются начать работу с определенной структурой, и Next.js не остается позади.
Хотя существует множество ресурсов, посвященных созданию аутентифицированных приложений с помощью Next.js. Ну, есть даже проект с открытым исходным кодом , который буквально обрабатывает аутентификацию с нуля.
Но рамки этой статьи не охватывают всю концепцию аутентификации. Мы выбираем только конкретный шаблон процесса аутентификации: поток «сброса пароля» и то, как его можно реализовать на стороне клиента — интерфейсе — веб-приложения.
В этой статье вы увидите, как эту функцию можно реализовать с помощью распространенного инструмента выборки данных Axios — встроенной функции динамических маршрутов Next.js и useRouter
хука.
С момента появления Интернета инженеры всегда стремились предоставить решения проблем, возникших в первые дни существования Интернета, и безопасность программного обеспечения в Интернете не является исключением.
Есть такая популярная поговорка: «Пользователи всегда будут забывать свои пароли», и это абсолютная правда. Многие люди даже боятся страницы «сброс пароля», потому что, если подумать, после того, как они потратили много времени, пытаясь угадать свои пароли — все безрезультатно — они либо расстроены, либо злы, когда попадают на эту страницу. конкретная страница.
Когда мы создаем пользовательские интерфейсы, мы также должны стараться сделать так, чтобы пользователям было приятно работать с ними. Как бы нам ни хотелось просто пройти процесс сброса пароля, UX этого процесса также должен быть приоритетным.
Общий поток процесса сброса пароля можно увидеть ниже.
Примечание . В целях безопасности и хорошего UX хорошо использовать текст, очень похожий на этот: «На ваш почтовый ящик отправлено электронное письмо. Пожалуйста, нажмите на ссылку, когда получите ее». Вы можете построить это предложение так, как считаете нужным, если оно не показывает, что введенный им адрес электронной почты или имя пользователя существует в базе данных. Такой подход не позволяет злоумышленникам узнать, существует ли вообще это электронное письмо, тем самым саботируя любые попытки фишинга, которые они могут предпринять с указанным адресом электронной почты. Что касается UX, текст не гарантирует пользователю, что введенные им учетные данные являются правильными. Это, в свою очередь, позволяет им дважды проверять любые учетные данные, которые они отправляют.
user_id
или, в данном случае, их адрес электронной почты.https://localhost:3000/reset-password/user-email/JWToken
Теперь, когда вы увидели, как устроен процесс «сброса пароля», давайте посмотрим, как его можно реализовать с помощью Next.js.
В этом разделе мы рассмотрим концепцию динамических маршрутов, проиллюстрировав ее структурой папок проекта Next.js, и посмотрим, как мы интегрируем ее в функцию «сброса пароля». Но сначала давайте настроим приложение Next.js.
npx create-next-app app-name
Приведенная выше команда делает это за нас. Команда Next.js уже выпустила новое обновление для фреймворка, а также добавила несколько новых папок и функций в структуру проекта по умолчанию. Однако мы не будем подробно останавливаться на этом аспекте, так как он выходит за рамки данной статьи. Вы можете прочитать больше об обновлениях здесь , если хотите.
Во фрагменте ниже вы увидите базовую структуру файлов, с которыми мы будем взаимодействовать в этой статье.
└── pages /
├── forgot-password/
│ └── [token]/
│ └── [email].js
├── _app.js
└── index.js
Выше вы увидите, что файлы в структуре папок довольно маленькие. Это потому, что я хочу быть максимально кратким в этой статье.
И поскольку реализация потока «сброса пароля» является нашей главной заботой, я думаю, будет лучше, если у нас будет меньше беспорядка. Теперь давайте немного познакомимся с этой структурой.
Вы заметите, что у нас есть forgot-password
папка в pages
каталоге, которая содержит некоторые файлы. Но соглашение об именах этих файлов сильно отличается от того, как называются другие файлы. Имена файлов — token и email.js — заключаются в пару квадратных скобок.
Папки и файлы с такими именами называются динамическими маршрутами, и, поскольку они находятся в pages
каталоге, они автоматически становятся маршрутами, доступными для браузера. Они являются динамическими, потому что значения, которые принимают эти маршруты, не статичны, а это означает, что они меняются со временем.
Этот шаблон именования файлов обычно можно увидеть в действии, когда вы решаете создать блог или когда взаимодействуете с данными, которые меняются в зависимости от типа пользователя, вошедшего в приложение. Вы можете посмотреть, как я использовал эту функцию Next.js, когда создавал свой блог . Вы также можете узнать больше об этом в документации Next.js.
В forgot-password
папке путь к пользовательскому интерфейсу, содержащему форму забытого пароля, можно получить здесь. Взгляните на это ниже.
http://localhost:3000/forgot-password/token/email
Поскольку это динамический маршрут, параметры token
и email
URL всегда будут меняться в зависимости от пользователя, пытающегося сбросить свой пароль. Токен и электронная почта для пользователя A будут отличаться от таковых для пользователя B.
Хук в Next.js useRouter
можно использовать для реализации множества практических интерфейсных реализаций пользовательского интерфейса — от общей идеи реализации активного элемента панели навигации с .pathname
ключом до более сложных функций.
Давайте теперь посмотрим, как мы можем читать параметры URL из динамических маршрутов с помощью useRouter
хука, не так ли? Для этого вам нужно сначала импортировать модуль на свою страницу/компонент.
import { useRouter } from 'next/router'
export default function PageComponent({ children }) {
const router = useRouter()
return (
<React.Fragment>
{/* page content falls below */}
<div>{children}</div>
</React.Fragment>
)
}
Фрагмент выше показывает основное использование хука. Поскольку нас интересуют параметры запроса URL, будет лучше, если мы деструктурируем query
метод хука вместо того, чтобы делать что-то вроде этого: router.query
. Мы просто сделаем что-то подобное ниже.
import { useRouter } from 'next/router'
const { query } = useRouter()
Теперь мы можем приступить к созданию переменных, в которых будут храниться нужные нам параметры URL. Фрагмент ниже показывает, как вы можете это сделать.
const token = query.token
const email = query.email
Обратите внимание, что значения query.token
и query.email
являются результатом имени файлов. Напомним из структуры папок в forgot-password
папке, где у нас есть [email].js
и [token]
файлы. Если вы переименуете эти файлы в [userEmail].js
и [userToken]
соответственно, шаблон назначения этих переменных станет примерно таким, как показано ниже.
const token = query.userToken
const email = query.userEmail
Вы всегда можете записать эти переменные в консоль, чтобы увидеть результат.
Теперь, когда вы поняли, как эти параметры получаются из URL-адреса, давайте начнем с построения структуры форм.
В этом разделе мы рассмотрим процесс создания формы и то, как вы можете использовать Axios для получения данных через произвольную конечную точку API. Мы не будем сосредотачиваться на стилизации этих форм и объяснении структуры. Я предполагаю, что вы уже знаете, как структурировать и стилизовать базовую форму React. Итак, давайте начнем с макета формы на маршруте «забыть пароль».
import React from 'react'
import axios from 'axios'
import { ErrModal, SuccessModal } from '../components/Modals'
export const DefaultResetPassword = () => {
const [email, setEmail] = React.useState('')
const [loading, setLoading] = React.useState(false)
const handleForgot = () => { } // we’ll see this later
return (
<div>
<form onSubmit={handleForgot} className="reset-password">
<h1>Forgot Password</h1>
<p>You are not alone. We’ve all been here at some point.</p>
<div>
<label htmlFor="email">Email address</label>
<input
type="email"
name="email"
id="email"
placeholder= “your email address”
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<button name="reset-pwd-button" className="reset-pwd">
{!loading ? ‘Get secure link’: ‘Sending...’}
</button>
</form>
</div>
)
}
Фрагмент выше показывает базовую структуру пользовательского интерфейса, которую вы увидите, когда доберетесь до маршрута забытого пароля. Вы заметите текст в теге абзаца под полужирным текстом «Забыли пароль».
<p>You are not alone. We’ve all been here at some point</p>
С таким типом текста, как приведенный выше, вы улучшаете пользовательский опыт людей, которые попадают на страницу забытого пароля вашего приложения. Вы уверяете их, что в том, что они забыли свой пароль (пароли), нет ничего страшного, так что не стоит расстраиваться по этому поводу.
Вам не обязательно использовать точный текст выше. Вы можете просто убедиться, что любой текст, который вы используете, имеет тон сопереживания .
Теперь давайте перейдем к важной части этой формы, где нам нужно объявить функцию, которая будет отправлять электронное письмо, которое пользователь вводит в поле ввода, на серверную часть.
import { authEndpoints } from '../endpoints'
export const DefaultResetPassword = () => {
const handleForgot = async (e) => {
e.preventDefault()
try {
setLoading(true)
const response = await axios({
method: 'POST',
url: authEndpoints.recover,
data: {
email,
},
headers: {
'Content-Type': 'application/json',
},
})
setResestSuccess(response.data.msg)
setLoading(false)
setResetError('')
} catch (error) {
setLoading(false)
const { data } = error.response
setResetError(data.msg)
setResestSuccess(null)
}
}
return <div>{/* ...previous form component */}</div>
}
Из приведенного выше фрагмента вы заметите, что мы импортируем конечную точку API, на которую мы будем отправлять запрос POST, и именно поэтому мы передаем ее как переменную url
ключу в методе Axios.
Запрос POST получает адрес электронной почты пользователя в качестве полезной нагрузки, которая, в свою очередь, будет проверена на сервере, и для этого адреса электронной почты будет создан JWT, который будет использоваться для авторизации процесса сброса пароля пользователя.
setResestSuccess(response.data.msg)
setLoading(false)
setResetError('')
catch (error) {
setLoading(false)
const { data } = error.response
setResetError(data.msg)
setResestSuccess(null)
}
Когда вы посмотрите на приведенный выше фрагмент, вы заметите, что мы используем некоторые уже объявленные функции обратного вызова состояния переменных состояния.
Примером может служить setLoading
функция, значение которой установлено true
в try
блоке. Затем его значение устанавливается равным false, когда данные были успешно отправлены. А если нет, у нас есть catch
блок, который «отлавливает» ошибку и отображает сообщение об ошибке, которое мы деструктурировали из конечной точки.
Вы также заметите, что в приведенном выше фрагменте есть несколько функций обратного вызова состояния, например setResestSuccess
и setResetError
.
Сеттеры получаются из объявления переменных состояния. См. их ниже.
import React from 'react'
import { ErrModal, SuccessModal } from '../components/Modals'
export const DefaultResetPassword = () => {
const [resetSuccess, setResestSuccess] = React.useState()
const [resetError, setResetError] = React.useState()
return (
<div>
{resetError ? <ErrModal message={resetError} /> : null}
{resetSuccess ? <SuccessModal message={resetSuccess} /> : null}
<form onSubmit={handleForgot} className="reset-password">
{/* form content */}
</form>
</div>
)
}
Сообщения об ошибках или успехах, полученные от серверной части, могут отображаться в пользовательском интерфейсе, чтобы пользователь знал о статусе своих действий.
Вы заметите, что мы используем пользовательские модальные компоненты для отображения сообщения. Эти компоненты получают сообщение в качестве реквизита, и их можно повторно использовать в кодовой базе. Взгляните на структуру компонентов ниже.
export const SuccessModal = ({ message }) => {
return (
<div className="auth-success-msg">
<p>{message}</p>
</div>
)
}
export const ErrModal = ({ message }) => {
return (
<div className="auth-err-msg">
<p>{message}</p>
</div>
)
}
Вы можете стилизовать эти компоненты уникальным образом, чтобы вы могли отличить модальное окно «ошибка» от модального окна «успех». Общепринятым соглашением является использование красного цвета для сообщений об ошибках и зеленого цвета для сообщений об успешном выполнении. Как вы решите стилизовать эти компоненты, полностью зависит от вас.
В дополнение ко всему, что было сказано, нам нужен способ проверки того, что правильный тип данных передается в качестве реквизита модальному компоненту. Этого можно добиться с помощью модуля «prop-type» в реакции.
propTypes.ErrModal = {
message: propTypes.string.isRequired,
}
propTypes.SuccessModal = {
message: propTypes.string.isRequired,
}
Процесс проверки типов в приведенном выше фрагменте гарантирует, что данные, которые получает компонент, должны быть строкой, а это обязательно. Если компонент не получит prop со строковым значением, React выдаст ошибку.
Теперь, когда мы рассмотрели важный аспект первой формы и строительные блоки того, что мы будем копировать в маршруте сброса пароля. Давайте начнем, взглянув на макет формы ниже.
import axios from "axios";
import React from “react”;
import Head from “next/head”;
import { useRouter } from "next/router";
import { SuccessModal, ErrModal } from "../components/Modals";
const ResetPassword = () => {
const [newPassword, setNewPassword] = React.useState("");
const [loading, setLoading] = React.useState(false);
const [resetPasswordSuccess, setResetPasswordSuccess] = React.useState();
const [resetPasswordError, setResetPasswordError] = React.useState();
const { query } = useRouter();
const token = query.token;
const email = query.email;
const resetPassword = () => { } // coming in later...
return (
<React.Fragment>
<Head>
<title>Reset your password</title>
</Head>
<div>
{email && token ? (
<div className="auth-wrapper">
{resetPasswordSuccess ? (
<SuccessModal message={resetPasswordSuccess} />
) : (
null
)}
{resetPasswordError ? (
<ErrModal message={resetPasswordError} />
) : (
null
)}
<form onSubmit={resetPassword} className="reset-password">
<h1>Reset Password</h1>
<p>Please enter your new password</p>
<div>
<label htmlFor="password">Password*</label>
<input
name="password"
type="password"
id="password"
placeholder="enter new pasword"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</input>
<button
name="reset-pwd-button"
className="reset-pwd"
>
{!loading ? "Reset" : "Processing..."}
</button>
</form>
</div>
) : (
<p>The page you're trying to get to isn't available</p>
)}
</div>
</React.Fragment>
);
};
Поскольку мы рассмотрели зачатки первой формы в предыдущем разделе, приведенный выше фрагмент содержит почти то же самое в предыдущей форме.
Вы можете видеть, как мы читаем параметры из URL-адреса, а также объявления ошибок сброса пароля и переменных успеха.
const [resetPasswordSuccess, setResetPasswordSuccess] = React.useState()
const [resetPasswordError, setResetPasswordError] = React.useState()
const { query } = useRouter()
const token = query.token
const email = query.email
Вы также заметите, как мы условно отображаем форму сброса пароля, проверяя , присутствуют ли переменные и в URL-адресе email
; token
если эти переменные ложны (т. е. их нет в URL-адресе), мы отображаем текст, который говорит, что страница, которую они ищут, недоступна.
{
email && token ? (
<div className="auth-wrapper">
<FormComponentt />
</div>
) : (
<p>The page you’re trying to get to isn’t available</p>
)
}
Теперь давайте взглянем на функцию-обработчик, которую мы будем использовать для отправки нового пароля пользователя — вместе с токеном и электронным письмом для проверки — на серверную часть через конечную точку API.
import { authEndpoints } from '../endpoints'
const resetPassword = async (e) => {
e.preventDefault()
try {
setLoading(true)
const response = await axios({
method: 'POST',
url: authEndpoints.resetPassword,
data: {
token,
email,
password: newPassword,
},
headers: {
'Content-Type': 'application/json',
},
})
setResetPasswordSuccess(response.data.msg)
setLoading(false)
setTimeout(() => {
router.push('/')
}, 4000)
setResetPasswordError('')
} catch (error) {
setLoading(false)
setResetPasswordError(error.response.data.msg)
setResetPasswordSuccess(null)
}
}
Приведенный выше фрагмент представляет собой асинхронную функцию-обработчик. Мы используем его для отправки запроса POST с новым паролем пользователя, токеном доступа и адресом электронной почты, которые мы получили из параметров запроса в сегменте URL.
setTimeout(() => {
router.push('/')
}, 4000)
Когда вы посмотрите на приведенный выше фрагмент, вы увидите, как мы используем setTimeout
метод в JavaScript и хук Next.js useRouter
для перенаправления пользователя на домашнюю страницу — которая в данном случае является страницей входа — через четыре секунды. (вы можете сократить этот период времени, если хотите), чтобы они могли снова войти в систему.
Это также увеличивает показатель хорошего взаимодействия с пользователем, поскольку не позволяет пользователю искать ссылку или кнопку, которая возвращает его на страницу входа.
Существует много информации о лучших практиках и замечательных шаблонах проектирования сброса пароля. Эта статья представляет собой всего лишь интерфейсную реализацию процесса сброса пароля, которая также влияет на удобство работы пользователей. Недостаточно просто создать функцию сброса пароля без учета UX людей, которые будут использовать эту функцию.
Спасибо за чтение. Я надеюсь, что эта статья была полезной!
Оригинальный источник статьи на https://www.smashingmagazine.com
#nextjs #axios #react #security
1632537859
Not babashka. Node.js babashka!?
Ad-hoc CLJS scripting on Node.js.
Experimental. Please report issues here.
Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.
Additional goals and features are:
Nbb requires Node.js v12 or newer.
CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).
Install nbb
from NPM:
$ npm install nbb -g
Omit -g
for a local install.
Try out an expression:
$ nbb -e '(+ 1 2 3)'
6
And then install some other NPM libraries to use in the script. E.g.:
$ npm install csv-parse shelljs zx
Create a script which uses the NPM libraries:
(ns script
(:require ["csv-parse/lib/sync$default" :as csv-parse]
["fs" :as fs]
["path" :as path]
["shelljs$default" :as sh]
["term-size$default" :as term-size]
["zx$default" :as zx]
["zx$fs" :as zxfs]
[nbb.core :refer [*file*]]))
(prn (path/resolve "."))
(prn (term-size))
(println (count (str (fs/readFileSync *file*))))
(prn (sh/ls "."))
(prn (csv-parse "foo,bar"))
(prn (zxfs/existsSync *file*))
(zx/$ #js ["ls"])
Call the script:
$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs
Nbb has first class support for macros: you can define them right inside your .cljs
file, like you are used to from JVM Clojure. Consider the plet
macro to make working with promises more palatable:
(defmacro plet
[bindings & body]
(let [binding-pairs (reverse (partition 2 bindings))
body (cons 'do body)]
(reduce (fn [body [sym expr]]
(let [expr (list '.resolve 'js/Promise expr)]
(list '.then expr (list 'clojure.core/fn (vector sym)
body))))
body
binding-pairs)))
Using this macro we can look async code more like sync code. Consider this puppeteer example:
(-> (.launch puppeteer)
(.then (fn [browser]
(-> (.newPage browser)
(.then (fn [page]
(-> (.goto page "https://clojure.org")
(.then #(.screenshot page #js{:path "screenshot.png"}))
(.catch #(js/console.log %))
(.then #(.close browser)))))))))
Using plet
this becomes:
(plet [browser (.launch puppeteer)
page (.newPage browser)
_ (.goto page "https://clojure.org")
_ (-> (.screenshot page #js{:path "screenshot.png"})
(.catch #(js/console.log %)))]
(.close browser))
See the puppeteer example for the full code.
Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet
macro is similar to promesa.core/let
.
$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)' 0.17s user 0.02s system 109% cpu 0.168 total
The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx
this adds another 300ms or so, so for faster startup, either use a globally installed nbb
or use $(npm bin)/nbb script.cljs
to bypass npx
.
Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.
To load .cljs
files from local paths or dependencies, you can use the --classpath
argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs
relative to your current dir, then you can load it via (:require [foo.bar :as fb])
. Note that nbb
uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar
in the namespace name becomes foo_bar
in the directory name.
To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:
$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"
and then feed it to the --classpath
argument:
$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]
Currently nbb
only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar
files will be added later.
The name of the file that is currently being executed is available via nbb.core/*file*
or on the metadata of vars:
(ns foo
(:require [nbb.core :refer [*file*]]))
(prn *file*) ;; "/private/tmp/foo.cljs"
(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"
Nbb includes reagent.core
which will be lazily loaded when required. You can use this together with ink to create a TUI application:
$ npm install ink
ink-demo.cljs
:
(ns ink-demo
(:require ["ink" :refer [render Text]]
[reagent.core :as r]))
(defonce state (r/atom 0))
(doseq [n (range 1 11)]
(js/setTimeout #(swap! state inc) (* n 500)))
(defn hello []
[:> Text {:color "green"} "Hello, world! " @state])
(render (r/as-element [hello]))
Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core
namespace is included with the let
and do!
macros. An example:
(ns prom
(:require [promesa.core :as p]))
(defn sleep [ms]
(js/Promise.
(fn [resolve _]
(js/setTimeout resolve ms))))
(defn do-stuff
[]
(p/do!
(println "Doing stuff which takes a while")
(sleep 1000)
1))
(p/let [a (do-stuff)
b (inc a)
c (do-stuff)
d (+ b c)]
(prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3
Also see API docs.
Since nbb v0.0.75 applied-science/js-interop is available:
(ns example
(:require [applied-science.js-interop :as j]))
(def o (j/lit {:a 1 :b 2 :c {:d 1}}))
(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1
Most of this library is supported in nbb, except the following:
:syms
.-x
notation. In nbb, you must use keywords.See the example of what is currently supported.
See the examples directory for small examples.
Also check out these projects built with nbb:
See API documentation.
See this gist on how to convert an nbb script or project to shadow-cljs.
Prequisites:
To build:
bb release
Run bb tasks
for more project-related tasks.
Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb
License: EPL-1.0
#node #javascript
1661335260
Узнайте, как реализовать функцию сброса пароля с помощью Axios, useRouter Hook и динамических маршрутов Next.js, отдавая приоритет UX.
Большинство пользователей, как правило, забывают свои пароли, поэтому создавайте возможности, которые всегда будут ценны для людей, использующих ваши продукты. В этой статье мы обсудим, как реализовать функцию сброса пароля с динамическими маршрутами Next.js, отдавая приоритет UX.
В общем смысле аутентификация представляет собой препятствие для многих людей, когда они пытаются начать работу с определенной структурой, и Next.js не остается позади.
Хотя существует множество ресурсов, посвященных созданию аутентифицированных приложений с помощью Next.js. Ну, есть даже проект с открытым исходным кодом , который буквально обрабатывает аутентификацию с нуля.
Но рамки этой статьи не охватывают всю концепцию аутентификации. Мы выбираем только конкретный шаблон процесса аутентификации: поток «сброса пароля» и то, как его можно реализовать на стороне клиента — интерфейсе — веб-приложения.
В этой статье вы увидите, как эту функцию можно реализовать с помощью распространенного инструмента выборки данных Axios — встроенной функции динамических маршрутов Next.js и useRouter
хука.
С момента появления Интернета инженеры всегда стремились предоставить решения проблем, возникших в первые дни существования Интернета, и безопасность программного обеспечения в Интернете не является исключением.
Есть такая популярная поговорка: «Пользователи всегда будут забывать свои пароли», и это абсолютная правда. Многие люди даже боятся страницы «сброс пароля», потому что, если подумать, после того, как они потратили много времени, пытаясь угадать свои пароли — все безрезультатно — они либо расстроены, либо злы, когда попадают на эту страницу. конкретная страница.
Когда мы создаем пользовательские интерфейсы, мы также должны стараться сделать так, чтобы пользователям было приятно работать с ними. Как бы нам ни хотелось просто пройти процесс сброса пароля, UX этого процесса также должен быть приоритетным.
Общий поток процесса сброса пароля можно увидеть ниже.
Примечание . В целях безопасности и хорошего UX хорошо использовать текст, очень похожий на этот: «На ваш почтовый ящик отправлено электронное письмо. Пожалуйста, нажмите на ссылку, когда получите ее». Вы можете построить это предложение так, как считаете нужным, если оно не показывает, что введенный им адрес электронной почты или имя пользователя существует в базе данных. Такой подход не позволяет злоумышленникам узнать, существует ли вообще это электронное письмо, тем самым саботируя любые попытки фишинга, которые они могут предпринять с указанным адресом электронной почты. Что касается UX, текст не гарантирует пользователю, что введенные им учетные данные являются правильными. Это, в свою очередь, позволяет им дважды проверять любые учетные данные, которые они отправляют.
user_id
или, в данном случае, их адрес электронной почты.https://localhost:3000/reset-password/user-email/JWToken
Теперь, когда вы увидели, как устроен процесс «сброса пароля», давайте посмотрим, как его можно реализовать с помощью Next.js.
В этом разделе мы рассмотрим концепцию динамических маршрутов, проиллюстрировав ее структурой папок проекта Next.js, и посмотрим, как мы интегрируем ее в функцию «сброса пароля». Но сначала давайте настроим приложение Next.js.
npx create-next-app app-name
Приведенная выше команда делает это за нас. Команда Next.js уже выпустила новое обновление для фреймворка, а также добавила несколько новых папок и функций в структуру проекта по умолчанию. Однако мы не будем подробно останавливаться на этом аспекте, так как он выходит за рамки данной статьи. Вы можете прочитать больше об обновлениях здесь , если хотите.
Во фрагменте ниже вы увидите базовую структуру файлов, с которыми мы будем взаимодействовать в этой статье.
└── pages /
├── forgot-password/
│ └── [token]/
│ └── [email].js
├── _app.js
└── index.js
Выше вы увидите, что файлы в структуре папок довольно маленькие. Это потому, что я хочу быть максимально кратким в этой статье.
И поскольку реализация потока «сброса пароля» является нашей главной заботой, я думаю, будет лучше, если у нас будет меньше беспорядка. Теперь давайте немного познакомимся с этой структурой.
Вы заметите, что у нас есть forgot-password
папка в pages
каталоге, которая содержит некоторые файлы. Но соглашение об именах этих файлов сильно отличается от того, как называются другие файлы. Имена файлов — token и email.js — заключаются в пару квадратных скобок.
Папки и файлы с такими именами называются динамическими маршрутами, и, поскольку они находятся в pages
каталоге, они автоматически становятся маршрутами, доступными для браузера. Они являются динамическими, потому что значения, которые принимают эти маршруты, не статичны, а это означает, что они меняются со временем.
Этот шаблон именования файлов обычно можно увидеть в действии, когда вы решаете создать блог или когда взаимодействуете с данными, которые меняются в зависимости от типа пользователя, вошедшего в приложение. Вы можете посмотреть, как я использовал эту функцию Next.js, когда создавал свой блог . Вы также можете узнать больше об этом в документации Next.js.
В forgot-password
папке путь к пользовательскому интерфейсу, содержащему форму забытого пароля, можно получить здесь. Взгляните на это ниже.
http://localhost:3000/forgot-password/token/email
Поскольку это динамический маршрут, параметры token
и email
URL всегда будут меняться в зависимости от пользователя, пытающегося сбросить свой пароль. Токен и электронная почта для пользователя A будут отличаться от таковых для пользователя B.
Хук в Next.js useRouter
можно использовать для реализации множества практических интерфейсных реализаций пользовательского интерфейса — от общей идеи реализации активного элемента панели навигации с .pathname
ключом до более сложных функций.
Давайте теперь посмотрим, как мы можем читать параметры URL из динамических маршрутов с помощью useRouter
хука, не так ли? Для этого вам нужно сначала импортировать модуль на свою страницу/компонент.
import { useRouter } from 'next/router'
export default function PageComponent({ children }) {
const router = useRouter()
return (
<React.Fragment>
{/* page content falls below */}
<div>{children}</div>
</React.Fragment>
)
}
Фрагмент выше показывает основное использование хука. Поскольку нас интересуют параметры запроса URL, будет лучше, если мы деструктурируем query
метод хука вместо того, чтобы делать что-то вроде этого: router.query
. Мы просто сделаем что-то подобное ниже.
import { useRouter } from 'next/router'
const { query } = useRouter()
Теперь мы можем приступить к созданию переменных, в которых будут храниться нужные нам параметры URL. Фрагмент ниже показывает, как вы можете это сделать.
const token = query.token
const email = query.email
Обратите внимание, что значения query.token
и query.email
являются результатом имени файлов. Напомним из структуры папок в forgot-password
папке, где у нас есть [email].js
и [token]
файлы. Если вы переименуете эти файлы в [userEmail].js
и [userToken]
соответственно, шаблон назначения этих переменных станет примерно таким, как показано ниже.
const token = query.userToken
const email = query.userEmail
Вы всегда можете записать эти переменные в консоль, чтобы увидеть результат.
Теперь, когда вы поняли, как эти параметры получаются из URL-адреса, давайте начнем с построения структуры форм.
В этом разделе мы рассмотрим процесс создания формы и то, как вы можете использовать Axios для получения данных через произвольную конечную точку API. Мы не будем сосредотачиваться на стилизации этих форм и объяснении структуры. Я предполагаю, что вы уже знаете, как структурировать и стилизовать базовую форму React. Итак, давайте начнем с макета формы на маршруте «забыть пароль».
import React from 'react'
import axios from 'axios'
import { ErrModal, SuccessModal } from '../components/Modals'
export const DefaultResetPassword = () => {
const [email, setEmail] = React.useState('')
const [loading, setLoading] = React.useState(false)
const handleForgot = () => { } // we’ll see this later
return (
<div>
<form onSubmit={handleForgot} className="reset-password">
<h1>Forgot Password</h1>
<p>You are not alone. We’ve all been here at some point.</p>
<div>
<label htmlFor="email">Email address</label>
<input
type="email"
name="email"
id="email"
placeholder= “your email address”
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<button name="reset-pwd-button" className="reset-pwd">
{!loading ? ‘Get secure link’: ‘Sending...’}
</button>
</form>
</div>
)
}
Фрагмент выше показывает базовую структуру пользовательского интерфейса, которую вы увидите, когда доберетесь до маршрута забытого пароля. Вы заметите текст в теге абзаца под полужирным текстом «Забыли пароль».
<p>You are not alone. We’ve all been here at some point</p>
С таким типом текста, как приведенный выше, вы улучшаете пользовательский опыт людей, которые попадают на страницу забытого пароля вашего приложения. Вы уверяете их, что в том, что они забыли свой пароль (пароли), нет ничего страшного, так что не стоит расстраиваться по этому поводу.
Вам не обязательно использовать точный текст выше. Вы можете просто убедиться, что любой текст, который вы используете, имеет тон сопереживания .
Теперь давайте перейдем к важной части этой формы, где нам нужно объявить функцию, которая будет отправлять электронное письмо, которое пользователь вводит в поле ввода, на серверную часть.
import { authEndpoints } from '../endpoints'
export const DefaultResetPassword = () => {
const handleForgot = async (e) => {
e.preventDefault()
try {
setLoading(true)
const response = await axios({
method: 'POST',
url: authEndpoints.recover,
data: {
email,
},
headers: {
'Content-Type': 'application/json',
},
})
setResestSuccess(response.data.msg)
setLoading(false)
setResetError('')
} catch (error) {
setLoading(false)
const { data } = error.response
setResetError(data.msg)
setResestSuccess(null)
}
}
return <div>{/* ...previous form component */}</div>
}
Из приведенного выше фрагмента вы заметите, что мы импортируем конечную точку API, на которую мы будем отправлять запрос POST, и именно поэтому мы передаем ее как переменную url
ключу в методе Axios.
Запрос POST получает адрес электронной почты пользователя в качестве полезной нагрузки, которая, в свою очередь, будет проверена на сервере, и для этого адреса электронной почты будет создан JWT, который будет использоваться для авторизации процесса сброса пароля пользователя.
setResestSuccess(response.data.msg)
setLoading(false)
setResetError('')
catch (error) {
setLoading(false)
const { data } = error.response
setResetError(data.msg)
setResestSuccess(null)
}
Когда вы посмотрите на приведенный выше фрагмент, вы заметите, что мы используем некоторые уже объявленные функции обратного вызова состояния переменных состояния.
Примером может служить setLoading
функция, значение которой установлено true
в try
блоке. Затем его значение устанавливается равным false, когда данные были успешно отправлены. А если нет, у нас есть catch
блок, который «отлавливает» ошибку и отображает сообщение об ошибке, которое мы деструктурировали из конечной точки.
Вы также заметите, что в приведенном выше фрагменте есть несколько функций обратного вызова состояния, например setResestSuccess
и setResetError
.
Сеттеры получаются из объявления переменных состояния. См. их ниже.
import React from 'react'
import { ErrModal, SuccessModal } from '../components/Modals'
export const DefaultResetPassword = () => {
const [resetSuccess, setResestSuccess] = React.useState()
const [resetError, setResetError] = React.useState()
return (
<div>
{resetError ? <ErrModal message={resetError} /> : null}
{resetSuccess ? <SuccessModal message={resetSuccess} /> : null}
<form onSubmit={handleForgot} className="reset-password">
{/* form content */}
</form>
</div>
)
}
Сообщения об ошибках или успехах, полученные от серверной части, могут отображаться в пользовательском интерфейсе, чтобы пользователь знал о статусе своих действий.
Вы заметите, что мы используем пользовательские модальные компоненты для отображения сообщения. Эти компоненты получают сообщение в качестве реквизита, и их можно повторно использовать в кодовой базе. Взгляните на структуру компонентов ниже.
export const SuccessModal = ({ message }) => {
return (
<div className="auth-success-msg">
<p>{message}</p>
</div>
)
}
export const ErrModal = ({ message }) => {
return (
<div className="auth-err-msg">
<p>{message}</p>
</div>
)
}
Вы можете стилизовать эти компоненты уникальным образом, чтобы вы могли отличить модальное окно «ошибка» от модального окна «успех». Общепринятым соглашением является использование красного цвета для сообщений об ошибках и зеленого цвета для сообщений об успешном выполнении. Как вы решите стилизовать эти компоненты, полностью зависит от вас.
В дополнение ко всему, что было сказано, нам нужен способ проверки того, что правильный тип данных передается в качестве реквизита модальному компоненту. Этого можно добиться с помощью модуля «prop-type» в реакции.
propTypes.ErrModal = {
message: propTypes.string.isRequired,
}
propTypes.SuccessModal = {
message: propTypes.string.isRequired,
}
Процесс проверки типов в приведенном выше фрагменте гарантирует, что данные, которые получает компонент, должны быть строкой, а это обязательно. Если компонент не получит prop со строковым значением, React выдаст ошибку.
Теперь, когда мы рассмотрели важный аспект первой формы и строительные блоки того, что мы будем копировать в маршруте сброса пароля. Давайте начнем, взглянув на макет формы ниже.
import axios from "axios";
import React from “react”;
import Head from “next/head”;
import { useRouter } from "next/router";
import { SuccessModal, ErrModal } from "../components/Modals";
const ResetPassword = () => {
const [newPassword, setNewPassword] = React.useState("");
const [loading, setLoading] = React.useState(false);
const [resetPasswordSuccess, setResetPasswordSuccess] = React.useState();
const [resetPasswordError, setResetPasswordError] = React.useState();
const { query } = useRouter();
const token = query.token;
const email = query.email;
const resetPassword = () => { } // coming in later...
return (
<React.Fragment>
<Head>
<title>Reset your password</title>
</Head>
<div>
{email && token ? (
<div className="auth-wrapper">
{resetPasswordSuccess ? (
<SuccessModal message={resetPasswordSuccess} />
) : (
null
)}
{resetPasswordError ? (
<ErrModal message={resetPasswordError} />
) : (
null
)}
<form onSubmit={resetPassword} className="reset-password">
<h1>Reset Password</h1>
<p>Please enter your new password</p>
<div>
<label htmlFor="password">Password*</label>
<input
name="password"
type="password"
id="password"
placeholder="enter new pasword"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</input>
<button
name="reset-pwd-button"
className="reset-pwd"
>
{!loading ? "Reset" : "Processing..."}
</button>
</form>
</div>
) : (
<p>The page you're trying to get to isn't available</p>
)}
</div>
</React.Fragment>
);
};
Поскольку мы рассмотрели зачатки первой формы в предыдущем разделе, приведенный выше фрагмент содержит почти то же самое в предыдущей форме.
Вы можете видеть, как мы читаем параметры из URL-адреса, а также объявления ошибок сброса пароля и переменных успеха.
const [resetPasswordSuccess, setResetPasswordSuccess] = React.useState()
const [resetPasswordError, setResetPasswordError] = React.useState()
const { query } = useRouter()
const token = query.token
const email = query.email
Вы также заметите, как мы условно отображаем форму сброса пароля, проверяя , присутствуют ли переменные и в URL-адресе email
; token
если эти переменные ложны (т. е. их нет в URL-адресе), мы отображаем текст, который говорит, что страница, которую они ищут, недоступна.
{
email && token ? (
<div className="auth-wrapper">
<FormComponentt />
</div>
) : (
<p>The page you’re trying to get to isn’t available</p>
)
}
Теперь давайте взглянем на функцию-обработчик, которую мы будем использовать для отправки нового пароля пользователя — вместе с токеном и электронным письмом для проверки — на серверную часть через конечную точку API.
import { authEndpoints } from '../endpoints'
const resetPassword = async (e) => {
e.preventDefault()
try {
setLoading(true)
const response = await axios({
method: 'POST',
url: authEndpoints.resetPassword,
data: {
token,
email,
password: newPassword,
},
headers: {
'Content-Type': 'application/json',
},
})
setResetPasswordSuccess(response.data.msg)
setLoading(false)
setTimeout(() => {
router.push('/')
}, 4000)
setResetPasswordError('')
} catch (error) {
setLoading(false)
setResetPasswordError(error.response.data.msg)
setResetPasswordSuccess(null)
}
}
Приведенный выше фрагмент представляет собой асинхронную функцию-обработчик. Мы используем его для отправки запроса POST с новым паролем пользователя, токеном доступа и адресом электронной почты, которые мы получили из параметров запроса в сегменте URL.
setTimeout(() => {
router.push('/')
}, 4000)
Когда вы посмотрите на приведенный выше фрагмент, вы увидите, как мы используем setTimeout
метод в JavaScript и хук Next.js useRouter
для перенаправления пользователя на домашнюю страницу — которая в данном случае является страницей входа — через четыре секунды. (вы можете сократить этот период времени, если хотите), чтобы они могли снова войти в систему.
Это также увеличивает показатель хорошего взаимодействия с пользователем, поскольку не позволяет пользователю искать ссылку или кнопку, которая возвращает его на страницу входа.
Существует много информации о лучших практиках и замечательных шаблонах проектирования сброса пароля. Эта статья представляет собой всего лишь интерфейсную реализацию процесса сброса пароля, которая также влияет на удобство работы пользователей. Недостаточно просто создать функцию сброса пароля без учета UX людей, которые будут использовать эту функцию.
Спасибо за чтение. Я надеюсь, что эта статья была полезной!
Оригинальный источник статьи на https://www.smashingmagazine.com
#nextjs #axios #react #security
1625674200
In this video, we are going to implement Google Analytics to our Next JS application. Tracking page views of an application is very important.
Google analytics will allow us to track analytics information.
Frontend: https://github.com/amitavroy/video-reviews
API: https://github.com/amitavdevzone/video-review-api
App link: https://video-reviews.vercel.app
You can find me on:
Twitter: https://twitter.com/amitavroy7
Discord: https://discord.gg/Em4nuvQk
#next js #js #react js #react #next #google analytics
1607768450
In this article, you will learn what are hooks in React JS? and when to use react hooks? React JS is developed by Facebook in the year 2013. There are many students and the new developers who have confusion between react and hooks in react. Well, it is not different, react is a programming language and hooks is a function which is used in react programming language.
Read More:- https://infoatone.com/what-are-hooks-in-react-js/
#react #hooks in react #react hooks example #react js projects for beginners #what are hooks in react js? #when to use react hooks
1598769720
Vue js Axios Tutorial is today’s leading topic. Axios is an excellent http client library. It uses promises by default and runs on both the client and the server. Axios is an impressive HTTP client library which lets you asynchronously issue HTTP requests to interact with REST endpoints. Consume REST APIs and make HTTP Requests is easy with Axios and Vue.js.
We will use Axios to send an HTTP request to the Node.js server and fetch the data from the database and display it on the client side. We will make GET and POST request to the Node.js server. At the backend, we will use Express web framework. Okay, so first we need to install Vue.js using CLI. If you are not familiar with Vue cli, then please check out this Vue cli Tutorial.
#axios and vue.js #axios #vue cli #node.js #vue js