将 AngularJS 应用程序中的 Facebook 登录与 Satellizer 集成
已发表: 2022-03-11随着 AngularJS 等功能丰富的前端框架的到来,越来越多的逻辑正在前端实现,例如数据操作/验证、身份验证等。 Satellizer,一个易于使用的 AngularJS 基于令牌的身份验证模块,简化了在 AngularJS 中实现身份验证机制的过程,该库内置了对 Google、Facebook、LinkedIn、Twitter、Instagram、GitHub、Bitbucket、Yahoo、Twitch 的支持和 Microsoft (Windows Live) 帐户。
在本文中,我们将构建一个与此处类似的非常简单的 webapp,它允许您登录并查看当前用户的信息。
身份验证与授权
一旦您的应用开始集成用户系统,您经常会遇到这两个可怕的词。 根据维基百科:
身份验证是确认实体声称为真的单个数据(数据)的属性的真实性的行为。
授权是指定对与信息安全和计算机安全相关的资源的访问权限的功能,特别是对访问控制的访问权限。
用外行的话来说,让我们举一个博客网站的例子,有些人在做这个网站。 博主撰写文章,经理验证内容。 每个人都可以验证(登录)系统,但他们的权限(授权)不同,因此博主无法验证内容,而管理员可以。
为什么选择卫星
您可以按照一些教程在 AngularJS 中创建自己的身份验证系统,例如这个非常详细的教程:JSON Web Token Tutorial: An Example in Laravel and AngularJS。 我建议阅读这篇文章,因为它很好地解释了 JWT(JSON Web 令牌),并展示了一种直接使用本地存储和 HTTP 拦截器在 AngularJS 中实现身份验证的简单方法。
那么为什么是卫星呢? 主要原因是它支持少数社交网络登录,例如 Facebook、Twitter 等。如今,尤其是对于在移动设备上使用的网站,输入用户名和密码非常麻烦,用户希望能够毫无障碍地使用您的网站通过使用社交登录。 由于集成每个社交网络的 SDK 并遵循他们的文档是相当重复的,因此以最小的努力支持这些社交登录会很好。
此外,Satellizer 是 Github 上的一个活跃项目。 Active 是这里的关键,因为这些 SDK 变化非常频繁,你不想时不时地阅读他们的文档(任何使用 Facebook SDK 的人都知道它有多烦人)
带有 Facebook 登录的 AngularJS 应用程序
这就是事情开始变得有趣的地方。
我们将构建一个具有常规登录/注册(即使用用户名、密码)机制并支持社交登录的网络应用程序。 这个 webapp 非常简单,因为它只有 3 个页面:
- 主页:任何人都可以看到
- 登录页面:输入用户名/密码
- 秘密页面:只有登录用户才能看到
对于后端,我们将使用 Python 和 Flask。 Python 和 Flask 框架非常有表现力,所以我希望将代码移植到其他语言/框架不会很难。 当然,我们将使用 AngularJS 作为前端。 对于社交登录,我们将仅与 Facebook 集成,因为它是目前最流行的社交网络。
开始吧!
第 1 步:引导项目
以下是我们将如何构建我们的代码:
- 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 环境。 下面是需要放入 requirements.txt 的 Python 模块列表:
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
安装这些库后,我们需要在index.html中包含 AngularJS 和 ui-router,并为 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:登录并注册
在此步骤结束时,您将拥有一个 Web 应用程序,您可以使用电子邮件和密码注册/登录。
第一步是配置后端。 我们需要一个用户模型和一种为给定用户生成 JWT 令牌的方法。 下面显示的 User 模型非常简化,甚至不执行任何基本检查,例如字段电子邮件是否包含“@”,或者字段密码是否包含至少 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')
我们使用python中的jwt模块在JWT中生成payload部分。 iat 和 exp 部分对应于令牌创建和过期的时间戳。 在此代码中,令牌将在 2 周后过期。
创建模型用户后,我们可以添加“登录”和“注册”端点。 两者的代码非常相似,所以这里我只展示“注册”部分。 请注意,默认情况下,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
添加 satellizer 作为依赖项:
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返回token对应的用户的user_id和email 。
@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 步:使用 Satellizer 登录 Facebook
正如开头提到的,Satellizer 的一个好处是它使集成社交登录变得更加容易。 在此步骤结束时,用户可以使用他们的 Facebook 帐户登录!
首先要做的是在 Facebook 开发者页面上创建一个应用程序,以获得一个application_id和一个密码。 如果您还没有 Facebook 开发者帐户,请按照 developers.facebook.com/docs/apps/register 创建一个 Facebook 开发者帐户并创建一个网站应用程序。 之后,您将拥有应用程序 ID 和应用程序密码,如下面的屏幕截图所示。
一旦用户选择连接 Facebook,Satellizer 将向端点/auth/facebook发送一个授权码。 使用此授权代码,后端可以从 Facebook /oauth端点检索访问令牌,允许调用 Facebook Graph API 以获取用户信息,例如位置、用户朋友、用户电子邮件等。
我们还需要跟踪用户帐户是通过 Facebook 创建的还是通过常规注册创建的。 为此,我们将facebook_id添加到我们的用户模型中。
facebook_id = db.Column(db.String(100))
facebook secret 是通过我们添加到app.config的环境变量 FACEBOOK_SECRET 配置的。
app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')
所以要启动app.py ,你应该设置这个环境变量:
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 登录非常简单。 首先,我们需要通过将这段代码添加到app.config函数中来告诉 Satellizer 我们的facebook_id :
$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 上查看代码
至此,webapp 的功能就已经完成了。 用户可以使用常规电子邮件和密码或使用 Facebook 登录/注册。 登录后,用户可以看到他的秘密页面。
制作漂亮的界面
此时界面不是很漂亮,所以让我们为布局和角度烤面包机模块添加一点Bootstrap,以很好地处理错误消息,例如登录失败时。
这个美化部分的代码可以在这里找到。
结论
本文展示了在(简单的)AngularJS webapp 中逐步集成 Satellizer。 使用 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 上的文档。