AngularJs 登录的简单实现

多数AngularJs应用离不开登录操作,最近阅读了一篇关于AngularJs登录的博客,博客中实现的登录系统demo能够应用于多数小型AngularJs应用,实现也并不困难,这里讲讲如何实现这个简单的登录系统。

种子项目

这里使用的种子项目是 angular-seed,登录系统会在这个种子项目的基础上完成

,github地址:https://github.com/angular/angular-seed/。按照github上README.md配置后便可在上面添加我们自己的登录系统。

angular-seed文件目录:

app/                    --> all of the source files for the application
app.css --> default stylesheet
components/ --> all app specific modules
version/ --> version related components
version.js --> version module declaration and basic "version" value service
version_test.js --> "version" value service tests
version-directive.js --> custom directive that returns the current app version
version-directive_test.js --> version directive tests
interpolate-filter.js --> custom interpolation filter interpolate-filter_test.js --> interpolate filter tests
view1/ --> the view1 view template and logic
view1.html --> the partial template
view1.js --> the controller logic
view1_test.js --> tests of the controller
view2/ --> the view2 view template and logic
view2.html --> the partial template
view2.js --> the controller logic
view2_test.js --> tests of the controller
app.js --> main application module
index.html --> app layout file (the main html template file of the app)
index-async.html --> just like index.html, but loads js files asynchronously
karma.conf.js --> config file for running unit tests with Karma
e2e-tests/ --> end-to-end tests
protractor-conf.js --> Protractor config file
scenarios.js --> end-to-end scenarios to be run by Protractor

这里,主要修改app.js以及view1文件夹相关文件,其中,view1将作为登录界面。

具体实现

实现登录表单

一个简单实用的登录表单的html文件:

<form name="loginForm" ng-controller="LoginController"
ng-submit="login(credentials)" novalidate> <label for="username">Username:</label>
<input type="text" id="username"
ng-model="credentials.username"> <label for="password">Password:</label>
<input type="password" id="password"
ng-model="credentials.password"> <button type="submit">Login</button> </form>

将该表单代码放入view1.html中,并且修改view1.js为该表单添加对应的controller,即LoginController.如下:

// controller
.controller('LoginController', function($scope, $rootScope, AUTH_EVENTS, AuthService) {
$scope.credentials = {
username : '',
password : ''
};
$scope.login = function(credentials) {
console.log('login', credentials);
AuthService.login(credentials).then(function(user) {
$rootScope.$broadcast(AUTH_EVENTS.loginSuccess);
$scope.$parent.setCurrentUser(user);
}, function() {
$rootScope.$broadcast(AUTH_EVENTS.loginFailed);
});
};
})

这里的credentials存放用户信息,值得注意的是:这里$scope.login仅完成抽象逻辑,具体的逻辑实现依靠AuthService这样的service,在controller里面建议多使用抽象逻辑,而非具体的实现。


用户登录状态记录

通常,用户的登录情况会放置在服务器端的Session中,当用户在应用内跳转页面时,相应的状态会保留在Session中。这里先定义__用户登录的状态__和__用户权限__,这里使用constants定义:

//用户登录状态
.constant('AUTH_EVENTS', {
loginSuccess: 'auth-login-success',
loginFailed: 'auth-login-failed',
logoutSuccess: 'auth-logout-success',
sessionTimeout: 'auth-session-timeout',
notAuthenticated: 'auth-not-authenticated',
notAuthorized: 'auth-not-authorized'
})

LoginController可以看出,constants可以像service一样方便注入;

//用户权限
.constant('USER_ROLES', {
all: '*',
admin: 'admin',
editor: 'editor',
guest: 'guest'
})

用户登录状态和用户权限将保存在Session中。


登录服务AuthService

将登录实现以及用户权限管理统一交给AuthService,可在顶层模块中注册该服务,这里是app.js中的myApp模块。

.factory('AuthService', function ($http, Session) {
var authService = {}; authService.login = function (credentials) { //本地提供的服务,可用loopback快速搭建
var api = $resource('http://localhost:3000/api/user_tests'); //因为没有写服务端验证用户密码,使用save是为了方便;
//这里,如果服务端已存在该credentials,返回的response会包含错误信息,可用来替代401、403等;
return api.save(credentials)
.$promise
.then(function(res) {
Session.create(res.id, res.id,
res.Role);
return res;
});
}; authService.isAuthenticated = function () {
return !!Session.userId;
}; authService.isAuthorized = function (authorizedRoles) {
if (!angular.isArray(authorizedRoles)) {
authorizedRoles = [authorizedRoles];
}
return (authService.isAuthenticated() &&
authorizedRoles.indexOf(Session.userRole) !== -1);
}; return authService;
})

Session

用户登录后,将服务器中关于用户的Session存储起来。

myApp模块中注册一个服务Session,用于存储服务端用户的Session。

.service('Session', function () {
this.create = function (sessionId, userId, userRole) {
this.id = sessionId;
this.userId = userId;
this.userRole = userRole;
};
this.destroy = function () {
this.id = null;
this.userId = null;
this.userRole = null;
};
})

用户信息

当用户登录之后,用户的信息(用户名、id等)应该保存在哪里?

这里的做法是将用户对象currentUser保存在应用顶层模块myApp$scope中,由于它位于$scope根部,应用中任何$scope都继承它,子代$scope可以很方便地使用根的变量和方法。

.controller('ApplicationController', function ($scope, USER_ROLES, AuthService) {
$scope.currentUser = null;
$scope.userRoles = USER_ROLES;
$scope.isAuthorized = AuthService.isAuthorized; $scope.setCurrentUser = function (user) {
$scope.currentUser = user;
};
})

首先声明currentUser以便在子代$scope中使用;因为在子代$scope中直接给currentUser赋值不会更新根部的currentUser,而是在当前$scope中新建一个currentUser(详细查询scope的继承),所以用setCurrentUser给根'$scope'的currentUser变量赋值。


访问控制

客户端不存在真正意义的访问控制,毕竟代码在客户端手中,这种工作通常是在服务端完成的,这里说的实际上是显示控制(visibility control).

AngularJs隐藏信息

ng-showng-hide是对DOM进行操作,会增加浏览器负担;这里选择使用ng-ifng-switch

view2.html中插入:

<div ng-if="currentUser">Welcome, {{ currentUser.name }}</div>
<div ng-if="isAuthorized(userRoles.admin)">You're admin.</div>
<div ng-switch on="currentUser.role">
<div ng-switch-when="userRoles.admin">You're admin.</div>
<div ng-switch-when="userRoles.editor">You're editor.</div>
<div ng-switch-default>You're something else.</div>
</div>

限制访问

有些页面仅允许具有权限的用户访问,这里需要限制其他用户的访问,在ui-router下可以通过传参进行限制,规定页面允许访问的角色:

.config(function ($stateProvider, USER_ROLES) {
$stateProvider.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard/index.html',
data: {
authorizedRoles: [USER_ROLES.admin, USER_ROLES.editor]
}
});
})

接下来,需要在每次页面改变前判断用户是否有权限访问,通过监听$stateChangeStart来实现:

.run(function ($rootScope, AUTH_EVENTS, AuthService) {
$rootScope.$on('$stateChangeStart', function (event, next) {
var authorizedRoles = next.data.authorizedRoles;
if (!AuthService.isAuthorized(authorizedRoles)) {
event.preventDefault();
if (AuthService.isAuthenticated()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
}
});
})

如果用户 未登录/无权限,将被限制在当前页面,发出 认证失败/授权失败 的广播;

之后,需要有相应的交互,如弹出登录框,提醒用户完成登录操作,或者弹出错误提示,告诉用户无权限访问相应的页面。

会话过期(Session expiration)

向服务器发送请求,如果出现非法访问等情况,服务端将返回HTTP response会包含相应的错误信息,例如:

  • 401 Unauthorized — 用户未登录
  • 403 Forbidden — 已登录,但无权限访问
  • 419 Authentication Timeout (non standard) — 会话过期
  • 440 Login Timeout (Microsoft only) — 会话过期

返回401、419、440时,需要弹出登录框让用户登录;

返回403时,需要弹出错误信息;

为了方便,这里的登录框使用Angulardirective封装,提供一个叫LoginDialog的标签。

.config(function ($httpProvider) {
$httpProvider.interceptors.push([
'$injector',
function ($injector) {
return $injector.get('AuthInterceptor');
}
]);
})
.factory('AuthInterceptor', function ($rootScope, $q,
AUTH_EVENTS) {
return {
responseError: function (response) {
$rootScope.$broadcast({
401: AUTH_EVENTS.notAuthenticated,
403: AUTH_EVENTS.notAuthorized,
419: AUTH_EVENTS.sessionTimeout,
440: AUTH_EVENTS.sessionTimeout
}[response.status], response);
return $q.reject(response);
}
};
})

loginDialog的实现如下,通过监听AUTH_EVENTS.notAuthenticatedAUTH_EVENTS.sessionTimeout,当用户 未登录/会话过期 时,将loginDialogvisible设为true,显示登录框:

.directive('loginDialog', function (AUTH_EVENTS) {
return {
restrict: 'A',
template: '<div ng-if="visible" ng-include="\'view1/view1.html\'">',
link: function (scope) {
var showDialog = function () {
scope.visible = true;
}; scope.visible = false;
scope.$on(AUTH_EVENTS.notAuthenticated, showDialog);
scope.$on(AUTH_EVENTS.sessionTimeout, showDialog)
}
};
})

为方便测试,将其放入index.html中:

<body ng-controller='ApplicationController'>
<div login-dialog ng-if="NotLoginPage"></div>
<ul class="menu">
<li><a href="#!/view1">view1</a></li>
<li><a href="#!/view2">view2</a></li>
</ul>
...

到这里,登录涉及的主要模块已经完成。

本文主要参考:https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec#.kr5puik92

AngularJs登录的更多相关文章

  1. angularjs &登录跳转

    如果要使用$location,$stateParams,那么必须有相应形参controller: function ($rootScope, $http, $scope, $state,$locati ...

  2. HTML+AngularJS+Groovy如何实现登录功能

    AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS核心特性有:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入等.AngularJS认为声明 ...

  3. AngularJS与ASP.NET MVC登录超时解决方案

    问题: 1.在Action中判断Ajax请求的方法Request.IsAjaxRequest()始终是false 2.返回给前台StatusCode和HttpUnauthorizedResult,前台 ...

  4. Angularjs 通过asp.net web api认证登录

    Angularjs 通过asp.net web api认证登录 Angularjs利用asp.net mvc提供的asp.net identity,membership实现居于数据库的用户名/密码的认 ...

  5. AngularJS之登录显示用户名

    效果图:在这里会显示出来用户名 使用AngularJs进行这样效果 第一步:写ng-app // 定义模块: var app = angular.module("pinyougou" ...

  6. AngularJS学习之 登录表单 清爽验证(边学边更新)

    注册过程的确好多需要验证的,但是注册成功之后的登录就简单多了, 只要用户 输入 用户名和密码, ajax向后台提交登录请求, 根据返回的结果确定用户名或者密码是否正确即可登录. 所以这个登录表单的验证 ...

  7. 四、angularjs 如何在页面没有登录的情况下阻止用户通过更改url进入页面--$stateChangeStart

    有时候用户没有登录或者在某些情况下你是不希望用户进入页面,但是angular的路由机制可以让用户直接通过更改Url进入页面,如何处理这一问题呢? ——监控路由转换机制 $stateChangeStar ...

  8. AngularJs学习——模拟用户登录的简单操作

    效果截图:

  9. angularjs 微信授权登录 微信支付

    最近做一个项目,用angular 一个单页应用,打算打包成 跨平台移动App 以及在微信里面使用.给大家一个案例 首先,熟悉一下微信授权部分的源代码,如下所示: javascript 前端代码: va ...

随机推荐

  1. winzip15.0注冊码

    username:Juzhaofeng 授权码:MPZRP-Y7LWW-K1DKG-FM92E-2C5F5-ZEKFF

  2. traceroute原理

    traceroute原理 ICMP ICMP全称为Internet Control Message Protocol,即,网络控制报文协议. 当一个IP数据报发送失败时,最后一个路由器会向发送发传递一 ...

  3. <!DOCTYPE html>的问题

    <!DOCTYPE> 声明必须位于 HTML5 文档中的第一行,也就是位于 <html> 标签之前.该标签告知浏览器文档所使用的 HTML 规范. doctype 声明不属于 ...

  4. 《第一行代码》学习笔记5-活动Activity(3)

    1.Menu:让菜单得到展示的同时,不占用任何屏幕的空间. public boolean onCreateOptionsMenu(Menu menu){ getMenuInflater().infla ...

  5. BULK INSERT如何将大量数据高效地导入SQL Server

    转载自:http://database.51cto.com/art/201108/282631.htm BULK INSERT如何将大量数据高效地导入SQL Server 本文我们详细介绍了BULK ...

  6. 【7】用Laravel5.1开发一个简单的博客系统

    声明: 本教程参考Jeffrey way 在laracasts.com上的视频教程,感谢Jeffrey way为大家带来的精彩教程,本教程如有侵权,请及时告知,联系邮箱wanglv93@gmail.c ...

  7. 使用grid++report打印选中行

    接上一篇<hibernate+spring+mvc+Easyui框架模式下使用grid++report的总结>对grid++report做进一步开发 先写一下实现流程: 1.默认为全部载入 ...

  8. IOS 开发-- 常用-- 核心代码

    网络请求 (包含block 和 delegate) 数据持久化技术 手势处理’ XML数据解析 多线程实现 核心动画编程 地图定位功能 CoreData数据持久化技术 本地通知和推送通知 常用宏定义 ...

  9. 开心菜鸟系列学习笔记-------javascript(3)

    一.原型链:     1)Object 是一个属性的集合,并且都拥有一个单独的原型对象.,这个原型对象object可以是一个object或者null值 2)不过一般来说,我们会使用__内部属性名__下 ...

  10. VC2010的破解方法(针对旗舰版)

    VS2010 正式版破解方法详解 全球开发者最为瞩目的Visual Studio 2010开发工具在4月12日正式发布,现为大家制作一个简单的破解教程有两种方法,操作不一样,原都一样(针对旗舰版,其他 ...