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

我是新的 AngularJS和通过他们的教程,并得到了它的感觉。

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

我想做的事
我想有我的项目 http://myproject.com单页。
B)一旦用户在浏览器中点击了 URL,根据用户是否登录,他将在同一个网址 http://myproject.com下看到一个主页/视图或登录页/视图。
如果用户没有登录,它会填写表单,服务器会在会话中设置一个 USER_TOKEN,因此所有对端点的进一步请求都将基于 USER_TOKEN进行身份验证

我的困惑
A)如何使用 AngularJS 处理客户端身份验证?我看到了 给你给你,但不知道如何使用它们
如何根据用户是否在相同的网址 http://myproject.com下登录,向用户显示不同的视图

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

179451 次浏览

I like the approach and implemented it on server-side without doing any authentication related thing on front-end

My 'technique' on my latest app is.. the client doesn't care about Auth. Every single thing in the app requires a login first, so the server just always serves a login page unless an existing user is detected in the session. If session.user is found, the server just sends index.html. Bam :-o

Look for the comment by "Andrew Joslin".

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

I think that every JSON response should contain a property (e.g. {authenticated: false}) and the client has to test it everytime: if false, then the Angular controller/service will "redirect" to the login page.

And what happen if the user catch de JSON and change the bool to True?

I think you should never rely on client side to do these kind of stuff. If the user is not authenticated, the server should just redirect to a login/error page.

I answered a similar question here: AngularJS Authentication + RESTful API


I've written an AngularJS module for UserApp that supports protected/public routes, rerouting on login/logout, heartbeats for status checks, stores the session token in a cookie, events, etc.

You could either:

  1. Modify the module and attach it to your own API, or
  2. Use the module together with UserApp (a cloud-based user management API)

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

If you use UserApp, you won't have to write any server-side code for the user stuff (more than validating a token). Take the course on Codecademy to try it out.

Here's some examples of how it works:

  • How to specify which routes that should be public, and which route that is the login form:

    $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'});
    

    The .otherwise() route should be set to where you want your users to be redirected after login. Example:

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

  • Login form with error handling:

    <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>
    
  • Signup form with error handling:

    <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>
    
  • Log out link:

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

    (Ends the session and redirects to the login route)

  • Access user properties:

    User properties are accessed using the user service, e.g: user.current.email

    Or in the template: <span>\{\{ user.email }}</span>

  • Hide elements that should only be visible when logged in:

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

  • Show an element based on permissions:

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

And to authenticate to your back-end services, just use user.token() to get the session token and send it with the AJAX request. At the back-end, use the UserApp API (if you use UserApp) to check if the token is valid or not.

If you need any help, just let me know!

I've created a github repo summing up this article basically: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login Github repo

Plunker

I'll try to explain as good as possible, hope I help some of you out there:

(1) app.js: Creation of authentication constants on app definition

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 Service: All following functions are implemented in auth.js service. The $http service is used to communicate with the server for the authentication procedures. Also contains functions on authorization, that is if the user is allowed to perform a certain action.

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) Session: A singleton to keep user data. The implementation here depends on you.

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) Parent controller: Consider this as the "main" function of your application, all controllers inherit from this controller, and it's the backbone of the authentication of this app.

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

(5) Access control: To deny access on certain routes 2 steps have to be implemented:

a) Add data of the roles allowed to access each route, on ui router's $stateProvider service as can be seen below (same can work for ngRoute).

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

b) On $rootScope.$on('$stateChangeStart') add the function to prevent state change if the user is not authorized.

$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) Auth interceptor: This is implemented, but can't be checked on the scope of this code. After each $http request, this interceptor checks the status code, if one of the below is returned, then it broadcasts an event to force the user to log-in again.

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);
}
};
} ]);

P.S. A bug with the form data autofill as stated on the 1st article can be easily avoided by adding the directive that is included in directives.js.

P.S.2 This code can be easily tweaked by the user, to allow different routes to be seen, or display content that was not meant to be displayed. The logic MUST be implemented server-side, this is just a way to show things properly on your ng-app.

In angularjs you can create the UI part, service, Directives and all the part of angularjs which represent the UI. It is nice technology to work on.

As any one who new into this technology and want to authenticate the "User" then i suggest to do it with the power of c# web api. for that you can use the OAuth specification which will help you to built a strong security mechanism to authenticate the user. once you build the WebApi with OAuth you need to call that api for token:

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;
 

};

and once you get the token then you request the resources from angularjs with the help of Token and access the resource which kept secure in web Api with OAuth specification.

Please have a look into the below article for more help:-

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

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;
 

};