Уроки, которые я извлек из недавнего проекта, который я делал, который включал создание аутентификации на основе токенов с использованием passport-jwt
.
До сих пор я всегда думал, что могу решить любой код, добавив в него кучу операторов `console.log`. На всякий случай мне приходилось применять отладчик или устанавливать точки останова, в основном это было для проверки переменных и в основном всегда для меньшего кода. Или код, с которым я был хорошо знаком (будь то написанный мной или кем-то другим).
Мой код аутентификации на основе токенов, который использовал passport-jwt
, не работал.
Вчера я очень долго застрял в куске кода. Намного дольше, чем я хотел бы признать.
Как работает аутентификация на основе токенов
- Пользователь регистрируется (его данные сохраняются в базе данных, и браузер получает токен)
- Этот токен должен быть включен браузером для доступа ко всем «защищенным» маршрутам (его можно включить в заголовок или тело запроса). Я консультировался с парой руководств, и все они использовали заголовок для передачи токена, поэтому я решил следовать тому же подходу.
Регистрация пользователя и отправка токена обратно в браузер работали нормально. Вчера я застрял, когда пользователь пытался получить доступ к защищенному маршруту.
Поскольку я еще не создал внешний интерфейс приложения, я собирался протестировать его с помощью Postman.
Все это фиаско началось с того, что я попытался отладить свой код, и закончилось тем, что я обнаружил, что имена заголовков не чувствительны к регистру: [SO](https://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive /5259004#5259004)
Конец истории я уже озвучил. Но в данном случае гораздо интереснее уроки, которые я усвоил на пути к открытию этого утверждения.
Метод декодирования токена, отправленного по запросу (это экспресс промежуточное ПО)
- Расшифруйте токен, чтобы получить полезные данные, которые использовались для создания токена. Для этого есть библиотеки и я решил использовать для этого паспорт-jwt
- Как только токен декодирован, вы получаете полезную нагрузку (userId или уникальное имя пользователя и т. д., которое в первую очередь используется для создания токена) и запрашиваете базу данных, чтобы найти пользователя, соответствующего токену. Если пользователь существует, у нас есть правильный токен, поэтому переходим к следующему обратному вызову. Если нет пользователя, соответствующего токену, мы не переходим к защищенному маршруту и отображаем «неавторизованный» (опять же это делает паспорт)
Код для защиты маршрута с помощью токена приведен ниже.
Я тестировал свой код с помощью Postman, где я отправлял свой веб-токен json (созданный при регистрации нового пользователя) в качестве заголовка авторизации.
Приведенный выше код выглядит довольно прямолинейно.
Когда я отправил запрос GET без заголовка, я не получил сообщения об ошибке, и ни один из операторов console.log в моей функции обратного вызова new JwtStrategy(jwtOptions, callback)
не сработал. Я был немного сбит с толку, но понял, что все в порядке, пока маршрут защищен.
Затем я сделал запрос GET от Postman на этот раз в своих заголовках, я добавил Authorization
в качестве ключа и свое значение как token
, которое я получил при регистрации пользователя.
Я был уверен, что смогу просмотреть защищенный маршрут. Я отправил токен, и моя реализация аутентификации токена была правильной (проверено из разных источников).
Но, к сожалению, я не увидел защищенный маршрут и не смог снова увидеть какие-либо операторы console.log
в функции обратного вызова.
Я был озадачен. Мои имена заголовков были такими же, конечно, я должен быть в состоянии аутентифицироваться. И даже если я не был аутентифицирован, я должен увидеть ошибку в операторе обратного вызова.
Мне потребовалось некоторое время, чтобы понять, что моя программа не выполняет обратный вызов и завершается сразу после jwtOptions
. Я пытался добавить кучу операторов console.log
во многих местах.
После этого я попытался решить проблему, изменив код наугад. Я добавил больше промежуточных программ (тот, что был до requireAuth
, я все еще оставил в коде, чтобы показать, что я пытался отладить проблему в худшем случае).
Я даже запустил отладчик с помощью VS Code (но подключил отладчик Chrome вместо Node.)
Наконец, в одной из итераций изменения кода, добавления дополнительных операторов отладчика и добавления дополнительных операторов console.log я изменил следующий фрагмент кода:
const jwtOptions = { jwtFromRequest: ExtractJwt.fromHeader('authorization'), secretOrKey: process.env.SECRET_KEY };
Я сделал ExtractJwt.fromHeader('authorization')
Просто изменил регистр ключа заголовка запроса, и код заработал.
До этого момента я часами пытался понять, что происходит с кодом, как использовать отладчик в VSCode (наконец-то мне удалось подключить Node как среду, но я не использовал отладку должным образом) и был просто рад, что это наконец-то заработало. .
Я закрыл ноутбук и положил этому конец.
Однако, когда я встал сегодня, несколько вещей не давали мне покоя.
Почему «авторизация» должна работать, а не «Авторизация» было первым делом.
Во-вторых, мои навыки отладки. Наконец-то мне удалось начать отладку в VS Code, но я не использовал его должным образом.
Поэтому я решил, что знаю, в чем проблема (более или менее), и хочу правильно использовать отладку, чтобы исследовать ее дальше.
Как запустить отладчик в VS Code
- Чтобы запустить отладчик для узла, выберите значок отладки слева.
- Он открывает панель отладки.
- Затем вверху есть небольшая кнопка «начать отладку», когда вы нажимаете ее, нас спрашивают, в каком контексте мы хотим отлаживать.
- Я выбираю Node, потому что хочу отлаживать экспресс-приложение.
- Он запускает код и прерывается в первой точке останова.
В моем случае я просто хочу сломаться, когда запускаю маршрут через Postman. Поэтому я просто продолжаю нажимать «Продолжить» на панели инструментов отладки.
Как только приложение больше не приостановлено, я запускаю защищенный маршрут через Postman. (А дальше начинается основная отладка)
Обратите внимание на отладчик VS Code: если мы изменим какую-либо переменную в моем коде, как я сделал "авторизацию" на "авторизацию", мы должны остановить отладчик и запустить его снова. (Может быть, я мог бы внести некоторые изменения в файл конфигурации, чтобы справиться с этим, но на данный момент я не знаю, как это сделать)
С помощью отладки узла я могу прервать маршрут GET или маршрут POST, а затем изучить и проверить, что происходит (это не происходило, когда я подключал отладчик Chrome).
С правильным кодом (то есть «авторизацией» в `jwtFromRequest: ExtractJwt.fromHeader('authorization')` ) я продолжал входить в соответствующий код после того, как наткнулся на точку останова для маршрута «/protected» (потому что это подходящее место, которое нам нужно чек.)
После долгих поисков кода я нашел место в коде паспорта, где извлекался «токен». Бинго, это то, что я хотел. Поэтому я добавил точку останова.
Затем я изменил «авторизацию», которая является правильным названием заголовка, на «Авторизация». К сожалению, пришлось перезапустить отладчик.
Но на этот раз я знал, какой раздел кода является важным, где мне нужно было проверить мой код дальше, потому что там у меня была точка останова. И бинго. Нет токена, когда мы называем заголовок авторизации Authorization
Я зашел в документацию passport-jwt
, прочитал еще немного и понял, что имена заголовков аутентификации могут быть любыми (это не было моим опытом. В конце концов, когда я использовал «Авторизация» вместо «авторизация», приложение не работало)
Итак, затем я изменил имя заголовка на Random
как в своем коде, так и в своем запросе почтальона, а затем перезапустил отладку. На этот раз, поскольку у меня была точка останова внутри кода passport
, где я знал, что должен был сломаться, мне не нужно было проходить весь этот код, и я мог просто нажимать кнопку continue
, пока не достигну точки останова в паспорте.
Затем, когда я достиг точки, где переменная token
извлекалась ExtractJwt
, я добавил переменные для наблюдения. Именно здесь я понял, что хотя имя моего заголовка было Random
(в моем коде, в моем запросе почтальона и даже в коде паспорта), request.headers
не имеет атрибута с именем Random
, но имеет атрибут с именем random
.
Таким образом, паспорт пытался извлечь токен из request.headers
с именем Random
, потому что это было имя, которое я указал для токена в своем коде, но не было атрибута с именем Random
.
В этот момент я понял, что что-то происходит, когда Почтальон устанавливал заголовки, и я погуглил и нашел ссылку, которую я дал в начале.
Таким образом, в основном заголовки были преобразованы в нижний регистр Почтальоном. Таким образом, ExtractJwt
не смог извлечь токен, потому что искал Random
, а не random
.
Раньше я использовал отладку, но с небольшими приложениями, которые я написал. В основном интерфейсные приложения и никогда раньше не использовалось экспресс-приложение.
Это был хороший опыт обучения. Долгое время для отладки ошибки, которую я, вероятно, никогда бы не обнаружил, если бы просто использовал строчные буквы для установки ключа заголовка авторизации.