В моем последнем посте я рассмотрел основы настройки проекта Vue.js. Вы можете посмотреть этот пост здесь. В этом посте я расскажу, как добавить авторизацию с помощью Auth0. Ознакомиться с завершенным репо можно здесь.
Настройка Auth0
Есть много вариантов обеспечения безопасности в приложениях. Для меня разгрузка деталей хранения имени пользователя / пароля, рабочих процессов смены пароля, регистрации новых пользователей и т. Д. Делает разработку моего приложения менее сложной. Команда Auth0 проделала невероятную работу по созданию инструментов, которые позволят вам как разработчику с легкостью добавить лучшую в своем классе безопасность в свои приложения. Основываясь на предыдущем проекте, давайте настроим Auth0 в приложении и создадим базовую страницу входа.
Вам нужно будет создать учетную запись с Auth0. После того, как у вас будет учетная запись, создайте нового клиента, щелкнув меню клиентов, а затем кнопку нового клиента. Это даст вам экран для имени клиента и выбора типа проекта.
После того, как вы нажмете «Создать», вы попадете на страницу быстрого старта, где вы сможете выбрать свой фреймворк, и они предоставят руководство по установке. Это поможет вам добавить Auth0 в приложение с помощью виджета блокировки. Я собираюсь использовать базовый сценарий Auth0 и настраиваемую страницу входа в этот проект. Виджет блокировки - отличный инструмент для быстрого начала работы, а руководство - лучший ресурс для его использования. Основное различие будет заключаться в поле входа в систему, руководство будет использовать виджет блокировки, и я предоставлю настраиваемую форму… любой из них будет обрабатывать запрос аналогичным образом.
На панели управления клиента Auth0 выберите вкладку настроек. Здесь вы найдете свой домен и идентификатор клиента. Эти значения понадобятся позже при вызове API через скрипт Auth0. Вам также нужно будет установить разрешенный URL-адрес обратного вызова. Для разработки это будет http: // localhost: 8080 (если вы используете vue-cli и не меняли порт).
Обязательно нажмите кнопку сохранения настроек внизу. Вы также захотите добавить пользователя для тестирования. Нажмите на опцию меню пользователей слева, а затем на кнопку создания пользователя. Введите данные и нажмите кнопку «Сохранить».
Затем давайте добавим в приложение Auth0. Есть несколько способов установить пакет, я буду ссылаться на него через cdn. В index.html добавьте следующую строку:
<!-- Auth0 --> <script src="https://cdn.auth0.com/w2/auth0-7.4.min.js"></script>
Настроить auth.js
Создайте файл в корне / src с именем auth.js. Здесь будет размещена логика авторизации для всего приложения. Сначала создайте экземпляр Auth0.
/* eslint no-undef: "off" */ const auth0 = new Auth0({ domain: 'YOUR DOMAIN', clientID: 'YOUR CLIENT ID', responseType: 'token', callbackURL: window.location.origin + '/' })
Вам нужно будет ввести данные своего домена и идентификатора клиента на вкладке настроек на панели инструментов Auth0. Если вы использовали шаблон веб-пакета из vue-cli, есть файлы конфигурации, где вы можете установить эти данные и указать на них через process.env.variable_name, ознакомьтесь с документацией здесь.
Этот файл экспортирует 4 вспомогательные функции, которые мы можем использовать в нашем приложении:
войти / выйти / checkAuth / requireAuth
// login let login = (username, password) => { auth0.login({ connection: 'Username-Password-Authentication', responseType: 'token', email: username, password: password, scope: 'openid email' }, function (err) { if (err) alert('something went wrong: ' + err.message) }) }
Функция входа в систему вызывает Auth0.login и передает ему объект конфигурации. Auth0 предоставляет несколько подключений для входа в социальные сети и предприятия, но здесь я использую подключение по имени пользователя и паролю. Тип ответа установлен на токен, что указывает на то, что мы хотим использовать JWT (веб-токен json) для нашей стратегии безопасности. Переменная области видимости сообщает auth0 детали, которые будут закодированы в jwt, в зависимости от вашего backend api вам может потребоваться добавить дополнительные данные для идентификации пользователя на стороне api.
// logout let logout = () => { localStorage.removeItem('id_token') localStorage.removeItem('profile') }
Функция выхода из системы просто удаляет id_token и элементы профиля из локального хранилища. Эти значения устанавливаются при успешном входе в систему (подробнее об этом чуть позже).
// checkAuth let checkAuth = () => { if (localStorage.getItem('id_token')) { return true } else { return false } }
Функция checkAuth просто возвращает true, если в локальном хранилище есть значение с идентификатором id_token. Для производственного приложения вы можете рассмотреть возможность декодирования токена и проверки, чтобы убедиться, что значение является токеном jwt и срок его действия не истек. Auth0 предлагает библиотеку jwt-decode, которая поможет в этом. Эта библиотека не будет проверять ваш токен (что происходит на стороне сервера), но ее можно использовать для подтверждения токена.
// requireAuth let requireAuth = (to, from, next) => { if (!checkAuth()) { console.log('auth failed ...') let path = '/login' let result = auth0.parseHash(window.location.hash) if (result && result.idToken) { // set token in local storage localStorage.setItem('id_token', result.idToken) // redirect to home page path = '/' // get user profile data auth0.getProfile(result.idToken, function (err, profile) { if (err) { // handle error alert(err) } let user = JSON.stringify(profile) localStorage.setItem('profile', user) }) } next({ path: path }) } else { next() } }
Эта функция вызывается из хука маршрутизатора beforeEnter, она получает переменную to и from и переменную обратного вызова с именем next. Это дает вам возможность проверить маршрут до загрузки компонента. Сначала мы вызываем функцию checkAuth, если она проходит, мы просто вызываем обратный вызов next () и позволяем всему продолжаться.
Если аутентификация не удалась, мы проверяем URL-адрес на предмет хэша и токена. Служба Auth0 по умолчанию настроена для использования метода перенаправления, поэтому все приложение перенаправляется обратно на URL-адрес обратного вызова в конфигурации Auth0. Поскольку этот хук beforeEnter установлен на нашем домашнем маршруте, мы можем проверить его на наличие хэша и токена и установить их в наше локальное хранилище. Мы также берем профиль пользователя и помещаем его в локальное хранилище. И последнее, что следует отметить в этой функции: у меня есть переменная с именем path, которая по умолчанию имеет значение / login. Если в URL-адресе есть хэш и токен, я сбрасываю путь, по которому пользователь возвращается домой (вы также можете отправить redirectUrl в Auth0 и вернуть пользователя туда, где они были, для простоты я отправляю их обратно на домашнюю страницу) . Если хеша или токена не было и проверка подлинности завершилась неудачно, пользователь попадет на страницу входа.
Наконец, экспортируйте функции:
export default { checkAuth, login, logout, requireAuth }
Создать контейнер для входа
Теперь, когда у нас настроена логика аутентификации, давайте создадим простую страницу входа. В папке с контейнерами добавьте новый файл с именем Login.vue.
<template> <div id="login"> <div class="columns"> <div class="column is-one-quarter"> <form @submit.prevent="login"> <p class="control"> <input class="input" v-model="email" placeholder="email"> </p> <p class="control"> <input class="input" v-model="pass" placeholder="password" type="password"> </p> <p> <button class="button" type="submit">Login</button> </p> </form> </div> </div> </div> </template> <script> import auth from '../auth' export default { name: 'login', data () { return { email: '', pass: '' } }, methods: { login () { auth.login(this.email, this.pass) } } } </script>
Здесь я использую простую форму с входными данными, привязанными через v-модель. Когда форма отправляется, метод входа в систему просто передает входные данные в функцию входа в систему аутентификации.
Регулировка роутера
Давайте внесем некоторые изменения в экземпляр маршрутизатора, чтобы вызвать функцию requireAuth в обработчике beforeEnter.
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) import login from './containers/Login' import home from './containers/Home' import dashboard from './containers/dashboard' import projects from './containers/projects' import auth from './auth' // application routes const routes = [ { path: '/login', component: login }, { path: '/', component: home, beforeEnter: auth.requireAuth }, { path: '/dashboard', component: dashboard, beforeEnter: auth.requireAuth }, { path: '/projects', component: projects, beforeEnter: auth.requireAuth } ] // export router instance export default new Router({ mode: 'history', routes, linkActiveClass: 'is-active' })
Настройте панель навигации
Затем необходимо обновить панель навигации, подключив кнопку выхода из системы и отображая только то, что пользователь вошел в систему. В компоненте navbar.vue добавьте следующие методы:
methods: { logout () { auth.logout() this.$router.go('/login') }, isLoggedIn () { return auth.checkAuth() } }
Обязательно импортируйте файл аутентификации:
импортируйте аутентификацию из "../auth"
Настройте событие нажатия на кнопку:
<a class="button" @click="logout()">Logout</a>
и, наконец, добавьте v-if на верхний уровень навигации:
<nav class="nav has-shadow" v-if="isLoggedIn()">
Заголовки авторизации в вызовах API
Теперь, когда наше приложение настроено для аутентификации наших пользователей, приложению необходимо отправить токен на наш api при выполнении защищенных вызовов. Чтобы это сработало, я установлю заголовок авторизации для объекта заголовка по умолчанию в axios.
Вернитесь в файл auth.js и в разделе, где он устанавливает токен в локальном хранилище, добавьте следующую строку:
// set auth headers axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('id_token')
Это добавляет заголовок авторизации к каждому вызову API, выполненному с помощью axios. Помимо установки значения при установке токена аутентификации, его необходимо установить при загрузке приложения (если есть токен). Добавьте следующие строки в файл auth.js вне функций:
// set auth header on start up if token is present if (localStorage.getItem('id_token')) { axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('id_token') }
Ответный перехватчик
Последний элемент, который нужно добавить, чтобы сделать его безопасным, - это создать перехватчик ответа, который выйдет из системы, если будет возвращен авторизованный вызов. В файле main.js добавьте следующее:
import auth from './auth' import axios from 'axios' axios.interceptors.response.use((response) => { return response }, function (error) { // Do something with response error if (error.response.status === 401) { console.log('unauthorized, logging out ...') auth.logout() router.replace('/login') } return Promise.reject(error) })
Если ответ возвращается и ошибка 401, вызовите auth.logout (). Затем пользователь будет перенаправлен на страницу входа.
Проверка на стороне сервера
Чтобы убедиться, что наша аутентификация работает, давайте создадим простой экспресс-сервер для его проверки. Создайте папку с именем server и добавьте файл с именем app.js. Вам нужно будет установить express, express-jwt и cors. Корс необходим, поскольку мы собираемся запустить экспресс-сервер на другом порту.
$ yarn add express express-jwt cors --dev OR $ npm install express express-jwt cors --save --dev
Я добавил флаг разработчика в приведенное выше утверждение, чтобы убедиться, что это только зависимости от разработки. Добавьте следующий код в файл server / app.js:
var express = require('express') var app = express() var cors = require('cors') var jwt = require('express-jwt') var jwtCheck = jwt({ secret: 'YOUR_CLIENT_SECRECT', audience: 'YOUR_CLIENT_ID' }) app.use(cors()) // check security for anything under the secured route app.use('/secured', jwtCheck) // open call app.get('/ping', function(req, res) { res.send("All good. You don't need to be authenticated to call this"); }) // secured call app.get('/secured/ping', function(req, res) { res.status(200).send("All good. You only get this message if you're authenticated"); }) app.listen(3000, function () { console.log('Example app listening on port 3000!') })
Здесь я создал простой экспресс-сервер с двумя маршрутами. Маршрут / ping не защищен и может быть вызван кем угодно. Любой путь под / secure будет обработан функцией jwtCheck, и действительный токен должен быть отправлен в заголовке запроса. Поэтому, если я звоню / защищаю / пингую, мне нужно будет предоставить действующий токен, чтобы получить ответ.
Вам нужно будет предоставить функции jwtCheck 2 переменные: секрет и аудиторию. Аудитория - это ваш идентификатор клиента с панели управления клиента в Auth0, а секрет - это секрет клиента с того же экрана. Чтобы получить более полный пример серверной части, просмотрите репозиторий экспресс-примеров Auth0 здесь.
Вернувшись к клиентской части нашего приложения, давайте внесем последние изменения, чтобы протестировать наше приложение. В файле main.js добавьте следующую строку, чтобы установить baseUrl для axios.
axios.defaults.baseURL = 'http://localhost:3000'
Затем в home.vue добавьте кнопку для вызова защищенного API:
<button type="button" class="button" v-on:click="testSecured()">Test API - Secured</button> methods: { testSecured: function () { console.log('sending secured test call to api ...') axios.get('/secured/ping').then((response) => { console.log(response) }, (response) => { console.log(response) }) } }
Запустите приложение и запустите сервер узлов [node server / app.js], войдите в систему и протестируйте, чтобы убедиться, что вы можете выполнять аутентифицированные вызовы. Кнопка запишет ответ на консоль:
Заключение
Теперь, когда мы обеспечили безопасность, мы можем предпринять следующие шаги для создания остальных экранов и функций. В следующем посте я расскажу о настройке vuex и использовании его для хранения данных профиля пользователя. Я также буду использовать для управления состоянием страницы наших проектов.