다음은 각도 기반 SPA에서 사용할 수있는 방법에 대한 설명입니다. 이 특정 예제는 토큰 기반 OAuth 인증을 사용하지만 다른 인증 체계에 적용될 수 있습니다. 그것은 느슨하게 Authentication in AngularJS (or similar) based application
일부 하이라이트에 기술 된 방법에 근거는 다음과 같습니다
인증은 auth
서비스를 통해 관리됩니다.
HTTP 요청을 차단하고 있습니다 :
401 (액세스 거부) 오류가 감지되고 어떤 사용자가 auth:login
이벤트가 방출되고, 로그인되지 않은 경우 - $rootScope
에 (주하지 방송)
사용자가 로그인되어 있고 OAuth 새로 고침 토큰을 사용할 수있는 동안 401 오류가 감지되면 새로 고침 토큰을 기반으로 새 액세스 토큰을 얻으려고합니다. auth:login
이벤트는 토큰을 새로 고칠 수없는 경우에만 생성됩니다.
일단 사용자가 로그인하면 서버가 사용자를 인증 할 수 있도록 사용자의 액세스 토큰을 포함하는 Authorization
헤더가 각 HTTP 요청에 삽입됩니다.
응용 프로그램은 auth:login
이벤트를보고 사용자에게 자격 증명을하라는 메시지를 표시합니다. (이 작업을 위해 Angular-UI Bootstrap modal dialog을 사용합니다.) 자격 증명이 제공되면 서비스의 login
함수를 호출하여 로그인을 완료해야합니다. login
이 호출 된 후 초기에 401 오류로 실패한 보류중인 모든 HTTP 요청이 다시 시도됩니다. 또는 auth
서비스의 loginCancelled
함수를 호출하여 로그인을 취소하면 보류중인 모든 HTTP 요청이 거부됩니다.
auth.login(userName, password, isPersistent)
.success(function() {
// Dismiss login dialog here
})
.error(function (data, status) {
if (status === 401 || (data && data.error === 'invalid_grant')) {
failureMessage = 'Log in failed: Bad username or password';
} else {
failureMessage = 'Log in failed: Unexpected error';
}
});
- 로그인 한 사용자
window.sessionStorage
에 저장된 세부 예 : : 사용자가 인증 정보를 제공 한 후 여기
angular.module('app', ['auth'])
.run(['$rootScope', 'auth', function ($rootScope, auth) {
$rootScope.$on(auth.options.loginRequiredEvent, function (event, details) {
// Display login dialog here, which will ultimately
// call `auth.login` or `auth.loginCancelled`
});
auth.restoreAuthDataFromStorage();
}]);
는 auth.login
호출의 예 또는 window.localStorage
(영구 로그인이 요청되었는지 여부에 따라) 페이지로드를 통해 액세스 할 수 있습니다.
마지막으로 여기에 auth
서비스 자체가 있습니다.
var module = angular.module('auth');
module.provider('auth', function() {
var authOptions = {
tokenUrl: '/OAuthToken',
loginRequiredEvent: 'auth:loginRequired',
logoffEvent: 'auth:logoff',
loginEvent: 'auth:login',
authTokenKey: 'auth:accessToken'
};
this.config = function (options) {
angular.extend(authOptions, options);
};
// Get the auth service
this.$get = ['$rootScope', '$http', '$q', function ($rootScope, $http, $q) {
var authData = {
// Filled as follows when authenticated:
// currentUserName: '...',
// accessToken: '...',
// refreshToken: '...',
};
var httpRequestsPendingAuth = new HttpRequestsPendingAuthQueue();
// Public service API
return {
login: login,
refreshAccessToken: refreshAccessToken,
loginCancelled: loginCancelled,
logoff: logoff,
currentUserName: function() { return authData.currentUserName; },
isAuthenticated: function() { return !!authData.accessToken; },
getAccessToken: function() { return authData.accessToken; },
restoreAuthDataFromStorage: restoreAuthDataFromStorage,
_httpRequestsPendingAuth: httpRequestsPendingAuth,
options: authOptions,
};
function isAuthenticated() {
return !!authData.accessToken;
};
function restoreAuthDataFromStorage() {
// Would be better to use an Angular service to access local storage
var dataJson = window.sessionStorage.getItem(authOptions.authTokenKey) || window.localStorage.getItem(authOptions.authTokenKey);
authData = (dataJson ? JSON.parse(dataJson) : {});
}
function accessTokenObtained(data) {
if (!data || !data.access_token) {
throw new Error('No token data returned');
}
angular.extend(authData, {
accessToken: data.access_token,
refreshToken: data.refresh_token
});
// Would be better to use an Angular service to access local storage
var storage = (authData.isPersistent ? window.localStorage : window.sessionStorage);
storage.setItem(authOptions.authTokenKey, JSON.stringify(authData));
httpRequestsPendingAuth.retryAll($http);
}
function login(userName, password, isPersistent) {
// Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded
var data = 'grant_type=password&username=' + encodeURIComponent(userName) + '&password=' + encodeURIComponent(password);
return $http
.post(authOptions.tokenUrl, data, { ignoreAuthFailure: true })
.success(function (data) {
authData = {
currentUserName: userName,
isPersistent: isPersistent
};
accessTokenObtained(data);
$rootScope.$emit(authOptions.loginEvent);
})
.error(function() {
logoff();
});
}
function refreshAccessToken() {
if (!authData.refreshToken) {
logoff();
return $q.reject('No refresh token available');
}
// Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded
var data = 'grant_type=refresh_token&refresh_token=' + encodeURIComponent(authData.refreshToken);
return $http
.post(authOptions.tokenUrl, data, { ignoreAuthFailure: true })
.success(function (data) { accessTokenObtained(data); })
.error(function() { logoff(); });
}
function loginCancelled() {
httpRequestsPendingAuth.rejectAll();
}
function logoff() {
// Would be better to use an Angular service to access local storage
window.sessionStorage.removeItem(authOptions.authTokenKey);
window.localStorage.removeItem(authOptions.authTokenKey);
if (isAuthenticated()) {
authData = {};
$rootScope.$emit(authOptions.logoffEvent);
}
}
// Class implementing a queue of HTTP requests pending authorization
function HttpRequestsPendingAuthQueue() {
var q = [];
this.append = function (rejection, deferred) {
q.push({ rejection: rejection, deferred: deferred });
};
this.rejectAll = function() {
while (q.length > 0) {
var r = q.shift();
r.deferred.reject(r.rejection);
}
};
this.retryAll = function ($http) {
while (q.length > 0) {
var r = q.shift();
retryRequest($http, r.rejection.config, r.deferred);
}
};
function retryRequest($http, config, deferred) {
var configToUse = angular.extend(config, { ignoreAuthFailure: true });
$http(configToUse)
.then(function (response) {
deferred.resolve(response);
}, function (response) {
deferred.reject(response);
});
}
}
}];
});
module.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(['$injector', '$rootScope', '$q', function ($injector, $rootScope, $q) {
var auth;
return {
// Insert an "Authorization: Bearer <token>" header on each HTTP request
request: function (config) {
auth = auth || $injector.get('auth');
var token = auth.getAccessToken();
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + token;
}
return config;
},
// Raise a "login required" event upon "401 access denied" responses on HTTP requests
responseError: function(rejection) {
if (rejection.status === 401 && !rejection.config.ignoreAuthFailure) {
var deferred = $q.defer();
auth = auth || $injector.get('auth');
auth._httpRequestsPendingAuth.append(rejection, deferred);
if (auth.isAuthenticated()) {
auth.refreshAccessToken().then(null, function() {
$rootScope.$emit(auth.options.loginRequiredEvent, { message: 'Login session has timed out. Please log in again.' });
});
} else {
// Not currently logged in and a request for a protected resource has been made: ask for a login
$rootScope.$emit(auth.options.loginRequiredEvent, { rejection: rejection });
}
return deferred.promise;
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
}]);
큰 질문입니다. 다행이 대답했습니다. 나는 그것을 추가 할 것이다 ** 나는 브리즈 컨트롤러에 로그인을하지 않을 것이다 **. 인증 및 권한 부여는 별도의 관심사입니다. – Ward