AngularJS:在单页​​应用程序中使用身份验证的基本示例


100

我是AngularJS的新手,并仔细阅读了他们的教程,并对此有所了解。

我为项目准备了一个后端,每个REST端点都需要进行身份验证。

我想做什么
a。)我想为我的项目准备一个页面http://myproject.com
b。)用户在浏览器中单击URL后,根据用户是否登录,将在同一URL下向他显示主页/视图或登录页面/视图http://myproject.com
c。)如果用户未登录,则填写该表单,服务器将设置一个USER_TOKENin会话,因此将根据以下身份验证对端点的所有其他请求USER_TOKEN

我的困惑
a。)如何使用AngularJS处理客户端身份验证?我在这里这里看到,但不明白如何使用它们
。b。)如何根据用户是否在同一URL下登录向用户显示不同的视图http://myproject.com

我是第一次使用angular.js,真的对如何开始感到困惑。非常感谢任何建议和/或资源。


请各位看看下面的文章frederiknakstad.com/...
阿贾伊Beniwal

1
@MichaelCalkins只是放置一个链接不是建设性的。您至少应该说出链接将要提供的内容。
Dave Gordon

我的b:AngularJS访问控制和身份验证coderwall.com/p/f6brkg
Michael J. Calkins

Answers:


48

我创建了一个github回购,基本上总结了这篇文章:https : //medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login Github回购

柱塞

我会尽力向您解释,希望对您有所帮助:

(1)app.js:在应用程序定义上创建身份验证常量

var loginApp = angular.module('loginApp', ['ui.router', 'ui.bootstrap'])
/*Constants regarding user login defined here*/
.constant('USER_ROLES', {
    all : '*',
    admin : 'admin',
    editor : 'editor',
    guest : 'guest'
}).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'
})

(2)验证服务: auth.js服务中实现了以下所有功能。$ http服务用于与服务器通信以进行身份​​验证过程。还包含授权功能,即是否允许用户执行特定操作。

angular.module('loginApp')
.factory('Auth', [ '$http', '$rootScope', '$window', 'Session', 'AUTH_EVENTS', 
function($http, $rootScope, $window, Session, AUTH_EVENTS) {

authService.login() = [...]
authService.isAuthenticated() = [...]
authService.isAuthorized() = [...]
authService.logout() = [...]

return authService;
} ]);

(3)会话:保留用户数据的单例。这里的实现取决于您。

angular.module('loginApp').service('Session', function($rootScope, USER_ROLES) {

    this.create = function(user) {
        this.user = user;
        this.userRole = user.userRole;
    };
    this.destroy = function() {
        this.user = null;
        this.userRole = null;
    };
    return this;
});

(4)父控制器:将其视为应用程序的“主要”功能,所有控制器均继承自该控制器,并且是此应用程序身份验证的基础。

<body ng-controller="ParentController">
[...]
</body>

(5)访问控制:要拒绝某些路由的访问,必须执行两个步骤:

a)在ui路由器的$ stateProvider服务上添加允许访问每个路由的角色的数据,如下所示(对于ngRoute也可以使用)。

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

b)在$ rootScope。$ on('$ stateChangeStart')上添加该函数,以防止未经授权的用户更改状态。

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

(6)身份验证拦截器:已实现,但无法在此代码的范围内进行检查。在每个$ http请求之后,此拦截器都会检查状态代码,如果返回以下内容之一,则它将广播事件以强制用户再次登录。

angular.module('loginApp')
.factory('AuthInterceptor', [ '$rootScope', '$q', 'Session', 'AUTH_EVENTS',
function($rootScope, $q, Session, 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);
        }
    };
} ]);

PS通过添加directives.js中包含的指令,可以轻松避免出现第一篇文章中所述的表单数据自动填充错误。

PS2此代码可由用户轻松调整,以允许查看不同的路线,或显示不希望显示的内容。逻辑必须在服务器端实现,这只是在ng-app上正确显示内容的一种方法。


1
我一直在遵循您的指南,以全神贯注于客户端逻辑。真的很棒!!我错过了一些有关手动销毁会话的信息,但是我们也必须尝试并打破问题!
Sebastialonso

~~不确定我是否正确理解这条线:authService.login() = [...]那些方括号将代表$http.get(url, {uID, pwd}?~~好吧,看着插栓,就像我说的那样XD
netalex

1
您可以扩展服务器端的答案吗?
查询

25

我喜欢这种方法,并在服务器端实现了它,而无需在前端进行任何与身份验证相关的事情

我最新的应用程序上的“技术”是..客户端不在乎Auth。应用中的每件事都需要首先登录,因此,除非在会话中检测到现有用户,否则服务器始终会提供登录页面。如果找到session.user,则服务器仅发送index.html。巴姆:-o

寻找“安德鲁·乔斯林”的评论。

https://groups.google.com/forum/?fromgroups=#!searchin/angular/authentication/angular/POXLTi_JUgg/VwStpoWCPUQJ


3
如果是网络API?我猜不到我的回答:(
Leandro De Mello Fagundes

1
如果要显示用户名怎么办?或者,如果您正在使用端点URL中的用户名与服务进行对话?
perrygeo

2
对不起,但我不明白答案。您如何按角度处理会话?session.user设置在哪里?你能举一个这样的代码示例吗?谢谢
弗朗索瓦·罗曼

4
会话是在客户端而不是服务器端处理的,客户端保存令牌并将其作为每个请求的一部分发送。服务器验证令牌并处理请求
Daydreamer 2013年

4
能够理解它的人可以为我们其他人编辑此答案吗?
阿罗兹·珍妮兹

14

我在这里回答了类似的问题:AngularJS身份验证+ RESTful API


我已经为UserApp编写了一个AngularJS模块,该模块支持受保护的/公共路由,登录/注销时重新路由,状态检查的心跳,将会话令牌存储在cookie中,事件等。

您可以:

  1. 修改模块并将其附加到您自己的API,或者
  2. 将该模块与UserApp(基于云的用户管理API)一起使用

https://github.com/userapp-io/userapp-angular

如果使用UserApp,则无需为用户资料编写任何服务器端代码(除了验证令牌外)。参加Codecademy上的课程尝试一下。

以下是有关其工作原理的一些示例:

  • 如何指定应公开的路由以及登录表单的路由:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    $routeProvider.when('/home', {templateUrl: 'partials/home.html'});

    .otherwise()路径应设置为您希望您的用户登录后重定向。例:

    $routeProvider.otherwise({redirectTo: '/home'});

  • 带有错误处理的登录表单:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
  • 带有错误处理的注册表单:

    <form ua-signup ua-error="error-msg">
      <input name="first_name" placeholder="Your name"><br>
      <input name="login" ua-is-email placeholder="Email"><br>
      <input name="password" placeholder="Password" type="password"><br>
      <button type="submit">Create account</button>
      <p id="error-msg"></p>
    </form>
  • 注销链接:

    <a href="#" ua-logout>Log Out</a>

    (结束会话并重定向到登录路由)

  • 访问用户属性:

    使用user服务访问用户属性,例如:user.current.email

    或在模板中: <span>{{ user.email }}</span>

  • 隐藏仅在登录时才可见的元素:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

  • 显示基于权限的元素:

    <div ua-has-permission="admin">You are an admin</div>

为了对您的后端服务进行身份验证,只需使用user.token()获取会话令牌并将其与AJAX请求一起发送即可。在后端,使用UserApp API(如果使用UserApp)检查令牌是否有效。

如果您需要任何帮助,请告诉我!


如何“修改模块并将其附加到您自己的API”
Pureferret

2

在angularjs中,您可以创建UI部件,服务,指令以及angularjs代表UI的所有部分。这是一项很好的技术。

作为任何刚接触此技术并希望对“用户”进行身份验证的人,我建议使用c#Web API的功能来实现。为此,您可以使用OAuth规范,这将帮助您建立强大的安全性机制来对用户进行身份验证。使用OAuth构建WebApi后,您需要调用该API以获得令牌:

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

并且一旦获得令牌,便可以在令牌的帮助下向angularjs请求资源,并使用OAuth规范访问在Web Api中保持安全的资源。

请查看以下文章以获取更多帮助:-

http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/


1

我认为每个JSON响应都应包含一个属性(例如{authenticated:false}),客户端必须每次对其进行测试:如果为false,则Angular控制器/服务将“重定向”到登录页面。

如果用户捕获了JSON并将布尔值更改为True,会发生什么?

我认为您不应该依赖客户端来进行此类操作。如果用户未通过身份验证,则服务器应仅重定向到登录/错误页面。


2
检查一下:github.com/witoldsz/angular-http-auth-拦截器检查服务器响应状态代码,如果它是403(“需要登录”),它将广播一个事件,因此您可以在应用程序中捕获它并显示登录框。
aherok 2013年

10
停止使用答案互相回复。这就是评论的目的!
苏联2013年

@aherok的建议,您的评论将被提升为答案,它将被及时选为最佳。剩下的只是噪音。
user237419 2014年

0

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.