本文概述
随着功能丰富的前端框架(例如AngularJS)的出现, 越来越多的逻辑正在前端实现, 例如数据操作/验证, 身份验证等。 Satellizer是AngularJS易于使用的基于令牌的身份验证模块, 它简化了在AngularJS中实施身份验证机制的过程。该库内置了对Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch的支持和Microsoft(Windows Live)帐户。
在本文中, 我们将构建一个非常简单的webapp, 类似于此处的webapp, 你可以登录并查看当前用户的信息。
身份验证与授权
一旦你的应用开始集成用户系统, 你通常会遇到2个可怕的词。根据维基百科:
认证是确认实体声称为真的单条数据(数据)属性的真实性的行为。
授权是指定通常对与信息安全和计算机安全有关的资源的访问权限, 特别是对访问控制的访问权限的功能。
用通俗易懂的术语, 让我们以一个博客网站为例, 其中有人在工作。博主撰写文章, 经理验证内容。每个人都可以向系统进行身份验证(登录), 但是他们的权限(授权)不同, 因此博客作者无法验证内容, 而经理可以。
为什么要使用Satellizer
你可以通过遵循一些教程(例如非常详细的教程)在AngularJS中创建自己的身份验证系统:JSON Web令牌教程:Laravel和AngularJS中的示例。我建议阅读这篇文章, 因为它很好地解释了JWT(JSON Web令牌), 并展示了一种直接使用本地存储和HTTP拦截器在AngularJS中实现身份验证的简单方法。
那为什么要使用Satellizer化器呢?主要原因是它支持少数社交网络登录, 例如Facebook, Twitter等。如今, 尤其是对于在移动设备上使用的网站, 键入用户名和密码非常麻烦, 并且用户希望能够无障碍地使用你的网站。通过使用社交登录。由于集成每个社交网络的SDK并遵循其文档非常重复, 因此以最少的努力支持这些社交登录将是很好的。
此外, Satellizer是Github上的一个活跃项目。主动是此处的关键, 因为这些SDK经常更改, 并且你不希望时不时地阅读其文档(使用Facebook SDK的人都知道这很烦人)
使用Facebook登录的AngularJS应用
这是事情开始变得有趣的地方。
我们将构建一个具有常规登录/注册(即使用用户名, 密码)机制并支持社交登录的网络应用。这个webapp非常简单, 因为它只有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环境。以下是放置在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(为简洁起见, 省略了import语句):
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上具有此基本接口
此时, “主页”, “登录”和”秘密”链接应该可以正常工作, 并显示相应模板的内容。
恭喜, 你刚刚完成了骨架的设置!如果遇到任何错误, 请在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')
我们在python中使用jwt模块在JWT中生成有效负载部分。 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}
})
$ dataChangeStart事件中将使用”数据”部分, 每次路由更改时都会触发该事件:
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:使用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的调用来获取用户信息, 例如位置, user_friends, 用户电子邮件等。
我们还需要跟踪用户帐户是使用Facebook还是通过常规注册创建的。为此, 我们将facebook_id添加到我们的用户模型。
facebook_id = db.Column(db.String(100))
通过我们添加到app.config的环境变量FACEBOOK_SECRET来配置facebook机密。
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服务器, 我们使用方便的模块请求。现在, 后端的困难部分已经完成。在前端, 添加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上检查代码
目前, 该Web应用程序在功能方面已经完成。用户可以使用常规电子邮件和密码或使用Facebook登录/注册。登录后, 用户可以看到其秘密页面。
制作漂亮的界面
此时界面还不是很漂亮, 所以让我们为布局和Angle Toaster模块添加一些Bootstrap, 以很好地处理错误消息, 例如登录失败时。
美化部分的代码可以在这里找到。
总结
本文显示了Satellizer在(简单的)AngularJS Webapp中的逐步集成。借助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上的文档。
相关:使用区块链的一键式登录:MetaMask教程