Torii — это легкая библиотека аутентификации для Ember.js. Он поддерживает различных поставщиков OAuth (таких как Twitter, Google или FaceBook) и может использоваться для реализации всплывающего потока перенаправления OAuth. Он использует диспетчер сеансов (для поддержки текущего пользователя) и адаптеры (для сохранения состояния аутентификации).
В этой статье я покажу, как реализовать вход в Twitter и как обрабатывать пользовательские сеансы с помощью Torii. Twitter использует OAuth 1.0a для аутентификации, которая не требует особых настроек на стороне клиента (только всплывающее окно и управление сеансами). Однако для этого требуется значительный серверный компонент, для которого я буду использовать Sinatra.
Для желающих следовать, вы можете взять код, сопровождающий эту статью из GitHub .
Настройка приложения в Twitter
Если вы хотите следовать, вам также нужно настроить приложение в Twitter. Вы можете сделать это, перейдя на http://apps.twitter.com , где вы нажимаете «Создать новое приложение». После этого заполните свои данные, убедившись, что введите http://127.0.0.1:9292
в поле URL обратного вызова (при условии, что вы проводите локальное тестирование).
После того как вы создали свое приложение, вы будете перенаправлены на страницу с настройками вашего приложения. Перейдите на вкладку «Ключи и токены доступа» и запишите свой ключ пользователя и его секрет.
Настройка сервера
Это требует немного знаний о том, как работает OAuth 1.0a (если вы хотите освежиться, вы можете проверить документацию на сайте Twitter ). Это также требует библиотеки, которая поддерживает аутентификацию с различными поставщиками OAuth. Поскольку мы используем Sinatra, OmniAuth является отличным выбором (он основан на Rack, поэтому будет работать практически во всех веб-приложениях на Ruby). Если бы мы использовали Node, мы могли бы вместо этого выбрать Passport .
Вместо того, чтобы просматривать настройки сервера, вы можете просто взять рабочую копию приложения и загрузить ее самостоятельно. Вот как:
git clone https://github.com/sitepoint-editors/torii-twitter-example.git cd torii-twitter-example
Затем в своем терминале добавьте свой ключ потребителя и свой секрет потребителя в свою среду
export TWITTER_KEY=twitter_key TWITTER_SECRET=twitter_secret
Запустите bundle
для установки любых зависимостей (предполагается, что у вас установлен Ruby), затем rake db:migrate
для настройки базы данных.
После этого вам нужно собрать приложение Ember:
cd public npm install && bower install ember build
Наконец, запустите rackup
чтобы запустить Sinatra и перейти к http://127.0.0.1:9292
. Если все прошло по плану, вы сможете войти в свое новое приложение через Twitter.
Обратите внимание, что конечные точки сервера:
Неаутентифицированный пользователь:
-
get '/auth/twitter'
: запускает вход в систему, запрашивает токен из Twitter, перенаправляет пользователя в Twitter для аутентификации. -
get '/auth/twitter/callback'
: Twitter аутентифицирует и отправляет токен здесь, сервер обменивает токен на токен доступа и аутентифицирует пользователя.
Аутентифицированный пользователь:
-
post '/logout'
: очищает аутентификацию пользователя. -
get '/users/me'
: возвращает аутентифицированную информацию о пользователе.
Теперь давайте используем наше приложение, чтобы посмотреть, как вы можете реализовать Torii в своих собственных проектах.
Установить тории
Сначала настройте проект Ember с Ember CLI и установите Torii (наш установлен в public
папке):
ember init npm install –save-dev torii
Настройте провайдера
Затем добавьте провайдера Twitter и установите requestTokenUri
к конечной точке сервера, с которой начинается поток: /auth/twitter
. Также установите sessionServiceName: 'session'
для настройки менеджера сеансов.
config/environment.js
ENV.torii = { sessionServiceName: 'session', providers: { 'twitter': { requestTokenUri: '/auth/twitter' } } };
В Torii есть несколько встроенных провайдеров , но создание собственного разработано очень просто.
Войти в систему
Далее настройте шаблон входа в систему. Если пользователь прошел аутентификацию, покажите его имя и ссылку для выхода. Если они не аутентифицированы, покажите ссылку для входа. Имеет смысл поместить это в шаблон application
чтобы он был виден на каждом маршруте.
app/templates/application.hbs
{{#if session.isWorking }} Working.. {{else}} {{#if session.isAuthenticated }} <p>Welcome {{session.currentUser.name}} <a href="#" {{action 'logout'}}>Logout</a> </p> {{else}} <a href="#" {{action 'signInViaTwitter'}}>Login via Twitter</a> {{/if}} {{/if}}
Свойство session
внедряется менеджером сеансов Torri и предоставляет несколько полезных свойств. session.isWorking
имеет значение true между такими переходами состояния, как opening
для authenticated
или closing
для unauthenticated
. Не используйте session
между переходами, но вместо этого показывайте полосу загрузки. session.currentUser
является аутентифицированным пользователем — он устанавливается адаптером.
Затем определите действие signInViaTwitter
которое откроет всплывающее окно и запустит знак OAuth в потоке.
app/routes/login.js
actions: { signInViaTwitter: function() { var route = this; this.get('session').open('twitter').then(function() { route.transitionTo('index'); }, function() { console.log('auth failed'); }); } }
Обратите внимание, что this.get('session').open('twitter')
возвращает обещание, которое разрешается после аутентификации пользователя, которое, в свою очередь, закрывает всплывающее окно и устанавливает сеанс. Как только пользовательский сеанс установлен, он переходит на индексный маршрут, тогда как, если он терпит неудачу, он ничего не делает.
Если пользователь обновляет браузер или открывает его в первый раз, когда сеанс активен, приложение должно извлечь существующий сеанс и продолжить, как если бы пользователь уже вошел в систему. Наконец, если пользователь выходит из системы, сеанс должен быть уничтожен. ,
app/routes/application.js
export default Ember.Route.extend({ beforeModel: function() { return this.get('session').fetch().then(function() { console.log('session fetched'); }, function() { console.log('no session to fetch'); }); }, actions: { logout: function() { this.get('session').close(); } } });
Здесь this.get('session').fetch()
выбирает существующий сеанс и устанавливает пользователя как аутентифицированного. Мы beforeModel
это в хук beforeModel
так что приложение будет ждать, пока пользователь не будет извлечен, перед рендерингом. Как и следовало ожидать, this.get('session').close()
закрывает сессию, что происходит, когда пользователь нажимает «Logout».
Управление сессиями и адаптер
Адаптер Torii обрабатывает проверку подлинности сервера и управляет сеансом пользователя тремя способами: open
, fetch
и close
. Они идут в папке app/torii-adapters
. По умолчанию используется адаптер приложения, который расширяет Ember.Object
.
Метод open
создает сеанс. Это происходит, когда наш сервер отправляет маркер аутентификации в приложение Ember (посредством перенаправления всплывающего окна) с параметром code
, например /?code=authentication_code
. Этот code
анализируется Torii и передается нашему адаптеру в параметре auth
. Чтобы кратко описать поток:
- Torii открывает всплывающее окно в
/auth/twitter
. - Сервер перенаправляет на Twitter.
- Пользователь аутентифицируется с помощью Twitter.
- Twitter перенаправляет на
/auth/twitter/callback
. - Сервер аутентифицирует пользователя и генерирует токен доступа.
- Сервер перенаправляет на наше приложение Ember с токеном доступа, например:
/?code=access_token
- Torii закрывает всплывающее окно, анализирует код и передает его адаптеру.
Как только токен становится доступным, он помещается в локальное хранилище и устанавливается заголовок авторизации для запросов Ajax. Аутентифицированный пользователь извлекается путем отправки запроса Ajax users/me
и сохраняется в сеансе.
app/torii-adapters/application.js
open: function(auth) { if (!auth.code) { return rejectPromise(); } localStorage.token = auth.code; var adapter = this.container.lookup('adapter:application'); adapter.set('headers', { 'Authorization': localStorage.token }); return this.get('store').find('user', 'me').then(function(user) { return { currentUser: user }; }); },
Параметр auth
содержит код — если он недоступен, обещание отклоняется и проверка подлинности не выполняется. Чтобы установить заголовки для Ember Data, используйте this.container.lookup
чтобы найти adapter:application
и установить заголовки для этого адаптера. this.get('store').find('user', 'me')
отправляет запрос users/me
, прежде чем объект со свойством currentUser
(установленным для аутентифицированного пользователя) будет возвращен. Torii добавит это к объекту session
который он будет внедрять во все маршруты, чтобы он был доступен в шаблонах.
Примечание : вам нужно определить пользовательскую модель с Ember CLI, чтобы сделать запрос к конечной точке users/me
с Ember Data:
ember g model user name:string token:string
Метод fetch
проверяет наличие токена в локальном хранилище. Если он существует, он выбирает токен и устанавливает сеанс. Это сохраняет аутентификацию пользователя между обновлениями страницы, пока токен действителен и остается в локальном хранилище.
fetch: function() { if (!localStorage.token) { return rejectPromise(); } var adapter = this.container.lookup('adapter:application'); adapter.set('headers', { 'Authorization': localStorage.token }); return this.get('store').find('user', 'me').then(function(user) { return { currentUser: user }; }); },
Метод получения аутентифицированного пользователя с токеном — это выборка пользователя из users/me
. Тории не знает, как вести сеансы, если вы предоставляете логику через ловушки адаптера. Пожалуйста, не стесняйтесь поделиться любыми альтернативными методами, которые у вас могут быть.
Наконец, метод close
закрывает сессию. Он удаляет токен из локального хранилища и post /logout
Ajax-запрос на post /logout
на сервер, что делает маркер доступа недействительным.
close: function() { var authToken = localStorage.token; localStorage.token = null; var adapter = this.container.lookup('adapter:application'); adapter.set('headers', { 'Authorization': authToken }); return new Ember.RSVP.Promise(function(resolve, reject) { Ember.$.ajax({ url: '/logout', headers: { 'Authorization': authToken }, type: 'POST', success: Ember.run.bind(null, resolve), error: Ember.run.bind(null, reject) }); }); }
Вывод
Мне потребовалось некоторое время, чтобы понять, как должна работать аутентификация для одностраничного приложения, не говоря уже о том, как работает OAuth. Это особенно верно, если приложение взаимодействует с REST API, который, как предполагается, не имеет состояния и, таким образом, не доступен сеанс на стороне сервера для сохранения пользователя. Поэтому я предпочитаю аутентификацию на основе токенов. Ember, к сожалению, не хватает в таких уроках, поэтому, если вы хотите узнать больше, вы должны искать другие фреймворки, такие как AngularJS.
Вот еще кое-что, что вы можете найти полезным: