在AngularJS应用中实现认证授权
在AngularJS应用中实现认证授权
在每一个严肃的应用中,认证和授权都是非常重要的一个部分。单页应用也不例外。应用并不会将所有的数据和功能都 暴露给所有的用户。用户需要通过认证和授权来查看应用的某个特定部分,或者在应用中进行特定的行为。为了在应用中对用户进行识别,我们需要让用户进行登录。
在用户管理方面,传统的服务器端应用和单页应用的实现方式有所不同,单页应用能够和服务器通信的方式只有AJAX。对于登录和退出来说也是如此。
负责识别用户的服务器端需要暴露出一个认证断电。单页应用将会把用户输入的信息发送到这个节点进行认证。在一个基于认证系统的典型token中,这 项服务用于在认证完毕之后获取一个token或者一个包含已登录用户的名字和角色信息的对象。客户端则需要在所有的安全API中获取这个token。
由于获取toekn的行为将会多次发生,我们最好将这个token存在客户端。在Angular中,我们可以将这个值存在一个服务中,因为服务在客 户端中是一个单体。但是,如果用户刷新了页面,服务中的值将会丢失。在这种情况下,最好将值存放在一个有浏览器提供的安全存储中,在这里我们要是用的是 sessionStorage,因为它在浏览器关闭时会自动被清空。
实现登录
我们现在来看一些代码。假设我们已经实现了所有的服务器端的逻辑,并且有一个叫做api/login
的REST接口进行登录认证,它将返回一个token。我们来写一个简单的服务用于用户登录。在后面我们会为这个服务逐渐添加功能:
app.factory("authenticationSvc", function($http, $q, $window) {
var userInfo;
function login(userName, password) {
var deferred = $q.defer();
$http.post("/api/login", {
userName: userName,
password: password
}).then(function(result) {
userInfo = {
accessToken: result.data.access_token,
userName: result.data.userName
};
$window.sessionStorage["userInfo"] = JSON.stringify(userInfo);
deferred.resolve(userInfo);
}, function(error) {
deferred.reject(error);
});
return deferred.promise;
}
return {
login: login
};
});
在实际的代码中,你可能会想要将存储的代码重构为一个单独的服务。在这里为了简单起见,我们只是将它放在他用一个服务中。这个服务可以被一个用于处理登录功能的控制器所用。
安全路由
我们需要在应用中设置一些安全路由。如果一个用户没有登录同时想要进入到某一个安全路由中,他应该被重定向到登录页。我们可以使用路由选项中的resolve
来实现这个功能。下面的代码片段展示了其中一种实现思路:
$routeProvider.when("/", {
templateUrl: "templates/home.html",
controller: "HomeController",
resolve: {
auth: ["$q", "authenticationSvc", function($q, authenticationSvc) {
var userInfo = authenticationSvc.getUserInfo();
if (userInfo) {
return $q.when(userInfo);
} else {
return $q.reject({ authenticated: false });
}
}]
}
});
resolve
块可以包含多个代码块,这些代码块将会在完成时返回promise对象。为了说明,上面代码中的auth
并不在框架中,而是我们自己定义的。你可以根据你的需求来进行修改。
通过或者拒绝路由的原因有很多种。在这里的情形中,你可以在解析/拒绝一个promise的时候传递一个对象。我们在服务中还没有实现getLoggedInUser()
方法。它是一个很简单的方法,能够从服务中返回loggedInUser
对象。
app.factory("authenticationSvc", function() {
var userInfo;
function getUserInfo() {
return userInfo;
}
});
通过上面的代码中的promise发送的想将会通过$rootScope进行广播。如果路由被解析,那么$routeChangeSuccess事件将会 被广播。然而,如果路由失败,那么事件$touteChangeError将会被广播。我们将监听$routeChangeError事件并将用户重定向 到登录页上。由于事件是在$rootScope层级上,最好在run
函数中绑定事件处理器。
app.run(["$rootScope", "$location", function($rootScope, $location) {
$rootScope.$on("$routeChangeSuccess", function(userInfo) {
console.log(userInfo);
});
$rootScope.$on("$routeChangeError", function(event, current, previous, eventObj) {
if (eventObj.authenticated === false) {
$location.path("/login");
}
});
}]);
处理页面刷新
当用户刷新页面时,服务将会失去现有状态。我们需要从浏览器的session storage中获取数据并将这些值赋值给loggerInUser变量。由于一个factory只会被调用一次,我们需要在一个初始化函数中设置这个变量,代码如下所示:
function init() {
if ($window.sessionStorage["userInfo"]) {
userInfo = JSON.parse($window.sessionStorage["userInfo"]);
}
}
init();
退出
当用户想要从应用中退出时,相应的API必须连同包含在请求头部中的token一起被调用。一旦用户退出,我们还需要清空sessionstorage中的数据。下面例子包含了一个退出函数,这个函数需要被添加到认证服务中。
function logout() {
var deferred = $q.defer();
$http({
method: "POST",
url: logoutUrl,
headers: {
"access_token": userInfo.accessToken
}
}).then(function(result) {
$window.sessionStorage["userInfo"] = null;
userInfo = null;
deferred.resolve(result);
}, function(error) {
deferred.reject(error);
});
return deferred.promise;
}
总结
单页应用的认证方式和传统web应用的认证方式非常不同。由于主要的工作都搬到了浏览器端,用户的状态也需要存储在客户端。重要的一点是要记住用户的状态也需要的服务器端保存和进行验证,因为骇客很可能慧聪客户端窃取用户的数据。
本文译自Implementing Authentication in Angular Applications,原文地址http://www.sitepoint.com/implementing-authentication-angular-applications/
- 学大伟业 国庆Day2
- ECMAScript 6入门 - 变量的解构赋值
- 二叉排序树 python实现
- ES6新特性概览
- 数据结构-顺序表的定义及python实现
- 洛谷P1516 青蛙的约会
- python实现二叉树的创建和遍历
- python中numpy模块下的np.clip()的用法
- Leetcode-Easy 543. Diameter of Binary Tree
- Leetcode-Easy 572. Subtree of Another Tree
- 图解javascript this指向什么?
- 2017/6/8-python正则表达式的使用
- 洛谷P1306 斐波那契公约数
- Angular开发实践(二):HRM运行机制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Flutter之SliverAppBar
- OpenGL ES 环境搭建
- Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署
- Asp.Net Core 程序部署到Linux(centos)生产环境(二):docker部署
- docker-compose 安装jenkins的正确姿势 实践笔记
- windows安装nginx注册为服务的正确姿势 并设置开机自启 实践笔记
- windows navicat连接oracle11G 自用 实践笔记
- docker安装官方redis集群并集群连接测试 的正确姿势 自用 实践笔记
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(一):Jenkins安装
- Centos7安装轻量级TCP转发工具rinetd注册为服务的正确姿势 并设置开机自启 实践笔记 自用
- Connection open error . Connection Timeout Expired. The timeout period elapsed during the post-login
- macbook-12-2015款 安装win10系统 自用 实践笔记
- OpenGL ES 之attribute
- windows server 2012 r2 搭建文件服务器 问题记录 自用 实践笔记
- OpenGL ES 之uniform和varying