Интеграция входа через Facebook в приложение AngularJS с помощью Satellizer
Опубликовано: 2022-03-11С появлением многофункциональных интерфейсных фреймворков, таких как AngularJS, все больше и больше логики внедряется во внешний интерфейс, например манипулирование/проверка данных, аутентификация и многое другое. Satellizer, простой в использовании модуль аутентификации на основе токенов для AngularJS, упрощает процесс реализации механизма аутентификации в AngularJS. Библиотека поставляется со встроенной поддержкой Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch. и учетные записи Microsoft (Windows Live).
В этой статье мы создадим очень простое веб-приложение, подобное приведенному здесь, которое позволит вам войти в систему и просмотреть информацию о текущем пользователе.
Аутентификация против авторизации
Это два страшных слова, с которыми вы часто сталкиваетесь, когда ваше приложение начинает интегрировать пользовательскую систему. Согласно Википедии:
Аутентификация — это действие по подтверждению истинности атрибута отдельного фрагмента данных (данного), который объект считает истинным.
Авторизация — это функция указания прав доступа к ресурсам, связанным с информационной и компьютерной безопасностью в целом и с разграничением доступа в частности.
С точки зрения непрофессионала, давайте возьмем пример веб-сайта блога, над которым работают несколько человек. Блогеры пишут статьи, а менеджер проверяет их содержание. Каждый человек может аутентифицироваться (войти в систему) в системе, но их права (авторизация) различны, поэтому блоггер не может проверять контент, а менеджер может.
Почему Сателлайзер
Вы можете создать свою собственную систему аутентификации в AngularJS, следуя некоторым учебным пособиям, таким как очень подробное: Учебное пособие по JSON Web Token: пример в Laravel и AngularJS. Я предлагаю прочитать эту статью, так как она очень хорошо объясняет JWT (веб-токен JSON) и показывает простой способ реализации аутентификации в AngularJS с использованием непосредственно локального хранилища и перехватчиков HTTP.
Итак, почему Satellizer? Основная причина заключается в том, что он поддерживает несколько входов в социальные сети, такие как Facebook, Twitter и т. д. В настоящее время, особенно для веб-сайтов, используемых на мобильных устройствах, ввод имени пользователя и пароля довольно громоздкий, и пользователи ожидают, что смогут использовать ваш веб-сайт без особых помех. с помощью социальных логинов. Поскольку интеграция SDK каждой социальной сети и следование их документации довольно повторяются, было бы неплохо поддерживать эти социальные входы с минимальными усилиями.
Более того, Satellizer является активным проектом на Github. Активность здесь ключевая, так как эти SDK меняются довольно часто, и вы не хотите время от времени читать их документацию (любой, кто работает с Facebook SDK, знает, как это раздражает).
Приложение AngularJS с входом через Facebook
Вот тут-то и начинается самое интересное.
Мы создадим веб-приложение, которое имеет обычный механизм входа/регистрации (т. е. с использованием имени пользователя и пароля), а также поддерживает вход через социальные сети. Это веб-приложение очень простое, так как в нем всего 3 страницы:
- Домашняя страница: каждый может видеть
- Страница входа: для ввода имени пользователя/пароля
- Секретная страница: ее могут видеть только авторизованные пользователи
Для серверной части мы будем использовать Python и Flask. Python и фреймворк Flask довольно выразительны, поэтому я надеюсь, что портирование кода на другие языки/фреймворки не составит большого труда. Мы, конечно же, будем использовать AngularJS для внешнего интерфейса. А для входа в социальные сети мы будем интегрироваться только с Facebook, поскольку в настоящее время это самая популярная социальная сеть.
Давайте начнем!
Шаг № 1: Bootstrap-проект
Вот как мы будем структурировать наш код:
- app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html
Весь внутренний код находится в app.py. Интерфейсный код помещается в папку static/. По умолчанию Flask автоматически обслуживает содержимое папки static/. Все частичные представления находятся в static/partials/ и обрабатываются модулем ui.router.
Чтобы начать программировать серверную часть, нам потребуется Python 2.7.* и установить необходимые библиотеки с помощью pip. Конечно, вы можете использовать virtualenv для изоляции среды Python. Ниже приведен список необходимых модулей Python для добавления в файл requirements.txt:
Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0
Чтобы установить все эти зависимости:
pip install -r requirements.txt
В app.py у нас есть исходный код для начальной загрузки Flask (операторы импорта опущены для краткости):
app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)
Затем мы инициализируем Bower и устанавливаем AngularJS и ui.router:
bower init # here you will need to answer some question. when in doubt, just hit enter :) bower install angular angular-ui-router --save # install and save these dependencies into bower.json
После установки этих библиотек нам нужно включить AngularJS и ui-router в index.html и создать маршруты для 3 страниц: домашняя, логин и секрет.
<body ng-app="DemoApp"> <a ui-sref="home">Home</a> <a ui-sref="login">Login</a> <a ui-sref="secret">Secret</a> <div ui-view></div> <script src="bower_components/angular/angular.min.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="main.js"></script> </body>
Ниже приведен код, который нам понадобится в main.js для настройки маршрутизации:
var app = angular.module('DemoApp', ['ui.router']); app.config(function ($stateProvider, $urlRouterProvider) { $stateProvider .state('home', { url: '/home', templateUrl: 'partials/home.tpl.html' }) .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', }) .state('login', { url: '/login', templateUrl: 'partials/login.tpl.html' }); $urlRouterProvider.otherwise('/home'); });
На этом этапе, если вы запускаете сервер python app.py , у вас должен быть этот базовый интерфейс по адресу http://localhost:5000.
Ссылки Home, Login и Secret должны работать на этом этапе и отображать содержимое соответствующих шаблонов.
Поздравляю, вы только что закончили настройку скелета! Если вы столкнулись с какой-либо ошибкой, проверьте код на GitHub.
Шаг № 2: Войдите и зарегистрируйтесь
В конце этого шага у вас будет веб-приложение, в котором вы можете зарегистрироваться/войти, используя адрес электронной почты и пароль.
Первым шагом является настройка бэкенда. Нам нужна модель пользователя и способ создания токена JWT для данного пользователя. Модель пользователя, показанная ниже, действительно упрощена и не выполняет даже каких-либо базовых проверок, таких как наличие в поле электронной почты «@» или наличие в поле пароля не менее 6 символов и т. д.
class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(100), nullable=False) password = db.Column(db.String(100)) def token(self): payload = { 'sub': self.id, 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(days=14) } token = jwt.encode(payload, app.config['TOKEN_SECRET']) return token.decode('unicode_escape')
Мы используем модуль jwt в python для создания части полезной нагрузки в JWT. Части iat и exp соответствуют временной метке создания и истечения срока действия токена. В этом коде срок действия токена истекает через 2 недели.
После создания модели User мы можем добавить конечные точки «login» и «register». Код для обоих очень похож, поэтому здесь я просто покажу часть «регистрации». Обратите внимание, что по умолчанию Satellizer будет вызывать конечные точки /auth/login и /auth/signup для «логина» и «регистрации» соответственно.
@app.route('/auth/signup', methods=['POST']) def signup(): data = request.json email = data["email"] password = data["password"] user = User(email=email, password=password) db.session.add(user) db.session.commit() return jsonify(token=user.token())
Давайте сначала проверим конечную точку с помощью curl:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
Результат должен выглядеть так:
{ "token": "very long string…." }
Теперь, когда серверная часть готова, давайте приступим к фронтенду! Во-первых, нам нужно установить satellizer и добавить его как зависимость в main.js:
bower install satellizer --save
Добавьте сателлизер в качестве зависимости:
var app = angular.module('DemoApp', ['ui.router', 'satellizer']);
Вход и регистрация в Satellizer на самом деле довольно просты по сравнению со всеми настройками до сих пор:
$scope.signUp = function () { $auth .signup({email: $scope.email, password: $scope.password}) .then(function (response) { // set the token received from server $auth.setToken(response); // go to secret page $state.go('secret'); }) .catch(function (response) { console.log("error response", response); }) };
Если у вас возникли трудности с настройкой кода, вы можете взглянуть на код на GitHub.

Шаг № 3: Но секретный вид на самом деле не секрет, потому что его может увидеть любой!
Да, это правильно! До сих пор любой желающий может зайти на секретную страницу без входа в систему.
Пришло время добавить какой-нибудь перехватчик в AngularJS, чтобы убедиться, что если кто-то перейдет на секретную страницу и если этот пользователь не вошел в систему, он будет перенаправлен на страницу входа.
Во-первых, мы должны добавить флаг requiredLogin, чтобы отличать секретную страницу от других.
.state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })
Часть «данные» будет использоваться в событии $stateChangeStart, которое запускается каждый раз при изменении маршрута:
app.run(function ($rootScope, $state, $auth) { $rootScope.$on('$stateChangeStart', function (event, toState) { var requiredLogin = false; // check if this state need login if (toState.data && toState.data.requiredLogin) requiredLogin = true; // if yes and if this user is not logged in, redirect him to login page if (requiredLogin && !$auth.isAuthenticated()) { event.preventDefault(); $state.go('login'); } }); });
Теперь пользователь не может напрямую перейти на секретную страницу без входа в систему. Ура!
Как обычно, код этого шага можно найти здесь.
Шаг № 4: Пришло время получить что-то действительно секретное!
На данный момент на секретной странице нет ничего действительно секретного. Давайте поместим туда что-то личное.
Этот шаг начинается с создания конечной точки в серверной части, которая доступна только для аутентифицированного пользователя, например, имеющего действительный токен. Конечная точка /user ниже возвращает user_id и адрес электронной почты пользователя, соответствующего токену.
@app.route('/user') def user_info(): # the token is put in the Authorization header if not request.headers.get('Authorization'): return jsonify(error='Authorization header missing'), 401 # this header looks like this: “Authorization: Bearer {token}” token = request.headers.get('Authorization').split()[1] try: payload = jwt.decode(token, app.config['TOKEN_SECRET']) except DecodeError: return jsonify(error='Invalid token'), 401 except ExpiredSignature: return jsonify(error='Expired token'), 401 else: user_id = payload['sub'] user = User.query.filter_by(id=user_id).first() if user is None: return jsonify(error='Should not happen ...'), 500 return jsonify(id=user.id, email=user.email), 200 return jsonify(error="never reach here..."), 500
Опять же, мы используем модуль jwt для декодирования токена JWT, включенного в заголовок «Авторизация», и для обработки случая, когда срок действия токена истек или он недействителен.
Давайте проверим эту конечную точку с помощью curl. Во-первых, нам нужно получить действительный токен:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
Затем с этим токеном:
curl localhost:5000/user -H "Authorization: Bearer {put the token here}"
Что дает этот результат:
{ "email": "[email protected]", "id": 1 }
Теперь нам нужно включить эту конечную точку в Secret Controller. Это довольно просто, так как нам просто нужно вызвать конечную точку, используя обычный модуль $http. Токен автоматически вставляется в заголовок Satellizer, поэтому нам не нужно заморачиваться со всеми подробностями сохранения токена и последующего размещения его в правильном заголовке.
getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }
Наконец-то у нас есть что-то действительно личное на секретной странице!
Код этого шага находится на GitHub.
Шаг № 5: Вход через Facebook через Satellizer
Как упоминалось в начале, Satellizer хорош тем, что он значительно упрощает интеграцию входа через социальные сети. В конце этого шага пользователи могут войти в систему, используя свою учетную запись Facebook!
Первое, что нужно сделать, это создать приложение на странице разработчиков Facebook, чтобы иметь application_id и секретный код. Пожалуйста, перейдите по ссылке Developers.facebook.com/docs/apps/register, чтобы создать учетную запись разработчика Facebook, если у вас ее еще нет, и создать приложение для веб-сайта. После этого у вас будет идентификатор приложения и секрет приложения, как на скриншоте ниже.
Как только пользователь решит подключиться к Facebook, Satellizer отправит код авторизации на конечную точку /auth/facebook . С помощью этого кода авторизации серверная часть может получить токен доступа из конечной точки Facebook /oauth , который позволяет вызову Facebook Graph API получать информацию о пользователе, такую как местоположение, user_friends, адрес электронной почты пользователя и т. д.
Нам также необходимо отслеживать, создается ли учетная запись пользователя через Facebook или посредством обычной регистрации. Для этого мы добавляем facebook_id в нашу модель пользователя.
facebook_id = db.Column(db.String(100))
Секрет facebook настраивается через переменные env FACEBOOK_SECRET, которые мы добавляем в app.config .
app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')
Итак, чтобы запустить app.py , вы должны установить эту переменную env:
FACEBOOK_SECRET={your secret} python app.py
Вот метод, который обрабатывает вход в Facebook. По умолчанию Satellizer вызывает конечную точку /auth/facebook .
@app.route('/auth/facebook', methods=['POST']) def auth_facebook(): access_token_url = 'https://graph.facebook.com/v2.3/oauth/access_token' graph_api_url = 'https://graph.facebook.com/v2.5/me?fields=id,email' params = { 'client_id': request.json['clientId'], 'redirect_uri': request.json['redirectUri'], 'client_secret': app.config['FACEBOOK_SECRET'], 'code': request.json['code'] } # Exchange authorization code for access token. r = requests.get(access_token_url, params=params) # use json.loads instead of urlparse.parse_qsl access_token = json.loads(r.text) # Step 2. Retrieve information about the current user. r = requests.get(graph_api_url, params=access_token) profile = json.loads(r.text) # Step 3. Create a new account or return an existing one. user = User.query.filter_by(facebook_id=profile['id']).first() if user: return jsonify(token=user.token()) u = User(facebook_id=profile['id'], email=profile['email']) db.session.add(u) db.session.commit() return jsonify(token=u.token())
Для отправки запроса на сервер Facebook воспользуемся удобным модулем Requests. Теперь трудная часть на бэк-энде сделана. Во внешнем интерфейсе добавить логин Facebook довольно просто. Во-первых, нам нужно сообщить Satellizer наш facebook_id , добавив этот код в функцию app.config :
$authProvider.facebook({ clientId: {your facebook app id}, // by default, the redirect URI is http://localhost:5000 redirectUri: 'http://localhost:5000/static/index.html' });
Чтобы войти с помощью Facebook, мы можем просто позвонить:
$auth.authenticate(“facebook”)
Как обычно, вы можете проверить код на GitHub.
На данный момент веб-приложение завершено с точки зрения функциональности. Пользователь может войти/зарегистрироваться, используя обычную электронную почту и пароль или используя Facebook. После входа в систему пользователь может увидеть свою секретную страницу.
Сделать красивый интерфейс
На данный момент интерфейс не очень красивый, поэтому давайте добавим немного Bootstrap для макета и модуль углового тостера, чтобы хорошо обрабатывать сообщения об ошибках, например, при сбое входа.
Код для этой украшающей части можно найти здесь.
Заключение
В этой статье показана пошаговая интеграция Satellizer в (простое) веб-приложение AngularJS. С помощью Satellizer мы можем легко добавить другие социальные сети, такие как Twitter, Linkedin и другие. Код на фронтенде точно такой же, как в статье. Однако серверная часть различается, поскольку SDK социальных сетей имеют разные конечные точки с разными протоколами. Вы можете взглянуть на https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py, который содержит примеры для Facebook, Github, Google, Linkedin, Twiter и Bitbucket. Если вы сомневаетесь, вам следует взглянуть на документацию на https://github.com/sahat/satellizer.