AngularJS UI 라우터 로그인 인증
저는 AngularJS를 처음 사용하고 다음 시나리오에서 angular- "ui-router"를 사용하는 방법에 대해 약간 혼란스러워합니다.
두 섹션으로 구성된 웹 응용 프로그램을 작성 중입니다. 첫 번째 섹션은 로그인 및 가입보기가있는 홈페이지이고 두 번째 섹션은 로그인에 성공한 대시 보드입니다.
index.html
각도 앱과 ui-router
구성을 사용하여 처리 /login
하고 /signup
볼 수 있는 홈 섹션을 만들었 dashboard.html
으며 앱과 ui-router
구성을 사용하여 많은 하위보기를 처리 하는 대시 보드 섹션에 대한 다른 파일 이 있습니다.
이제 대시 보드 섹션을 마쳤으며 두 섹션을 다른 각도 앱과 결합하는 방법을 모르겠습니다. 홈 앱에 대시 보드 앱으로 리디렉션하도록 지시하려면 어떻게해야합니까?
더 나은 데모를 만들고 이러한 서비스 중 일부를 사용 가능한 모듈로 정리하는 과정에 있습니다. 그러나 여기에 제가 제시 한 내용이 있습니다. 이것은 일부 경고를 해결하기위한 복잡한 프로세스이므로 여기에 연결하십시오. 이것을 여러 조각으로 나누어야합니다.
먼저, 사용자의 신원을 저장하는 서비스가 필요합니다. 나는 이것을 부른다 principal
. 사용자가 로그인했는지 확인할 수 있으며 요청시 사용자 ID에 대한 필수 정보를 나타내는 객체를 확인할 수 있습니다. 필요한 것은 무엇이든 가능하지만 필수는 표시 이름, 사용자 이름, 전자 메일 및 사용자가 속한 역할 (앱에 적용되는 경우)입니다. 교장은 역할 점검을 수행하는 방법도 있습니다.
.factory('principal', ['$q', '$http', '$timeout',
function($q, $http, $timeout) {
var _identity = undefined,
_authenticated = false;
return {
isIdentityResolved: function() {
return angular.isDefined(_identity);
},
isAuthenticated: function() {
return _authenticated;
},
isInRole: function(role) {
if (!_authenticated || !_identity.roles) return false;
return _identity.roles.indexOf(role) != -1;
},
isInAnyRole: function(roles) {
if (!_authenticated || !_identity.roles) return false;
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) return true;
}
return false;
},
authenticate: function(identity) {
_identity = identity;
_authenticated = identity != null;
},
identity: function(force) {
var deferred = $q.defer();
if (force === true) _identity = undefined;
// check and see if we have retrieved the
// identity data from the server. if we have,
// reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// otherwise, retrieve the identity data from the
// server, update the identity object, and then
// resolve.
// $http.get('/svc/account/identity',
// { ignoreErrors: true })
// .success(function(data) {
// _identity = data;
// _authenticated = true;
// deferred.resolve(_identity);
// })
// .error(function () {
// _identity = null;
// _authenticated = false;
// deferred.resolve(_identity);
// });
// for the sake of the demo, fake the lookup
// by using a timeout to create a valid
// fake identity. in reality, you'll want
// something more like the $http request
// commented out above. in this example, we fake
// looking up to find the user is
// not logged in
var self = this;
$timeout(function() {
self.authenticate(null);
deferred.resolve(_identity);
}, 1000);
return deferred.promise;
}
};
}
])
둘째, 사용자가 가고 싶은 상태를 확인하고, 로그인했는지 (필요한 경우, 로그인, 비밀번호 재설정 등에 필요하지 않음) 확인한 다음 역할 확인 (앱인 경우)을 수행하는 서비스가 필요합니다 필요합니다). 인증되지 않은 경우 로그인 페이지로 보내십시오. 인증되었지만 역할 확인에 실패하면 액세스 거부 페이지로 보내십시오. 이 서비스를 호출합니다 authorization
.
.factory('authorization', ['$rootScope', '$state', 'principal',
function($rootScope, $state, principal) {
return {
authorize: function() {
return principal.identity()
.then(function() {
var isAuthenticated = principal.isAuthenticated();
if ($rootScope.toState.data.roles
&& $rootScope.toState
.data.roles.length > 0
&& !principal.isInAnyRole(
$rootScope.toState.data.roles))
{
if (isAuthenticated) {
// user is signed in but not
// authorized for desired state
$state.go('accessdenied');
} else {
// user is not authenticated. Stow
// the state they wanted before you
// send them to the sign-in state, so
// you can return them when you're done
$rootScope.returnToState
= $rootScope.toState;
$rootScope.returnToStateParams
= $rootScope.toStateParams;
// now, send them to the signin state
// so they can log in
$state.go('signin');
}
}
});
}
};
}
])
이제 모든 사용자는가에 경청 할 필요가 ui-router
의 $stateChangeStart
. 이를 통해 현재 상태, 가고 싶은 상태를 검사하고 권한 부여 검사를 삽입 할 수 있습니다. 실패하면 경로 전환을 취소하거나 다른 경로로 변경할 수 있습니다.
.run(['$rootScope', '$state', '$stateParams',
'authorization', 'principal',
function($rootScope, $state, $stateParams,
authorization, principal)
{
$rootScope.$on('$stateChangeStart',
function(event, toState, toStateParams)
{
// track the state the user wants to go to;
// authorization service needs this
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
// if the principal is resolved, do an
// authorization check immediately. otherwise,
// it'll be done when the state it resolved.
if (principal.isIdentityResolved())
authorization.authorize();
});
}
]);
사용자의 신원을 추적하는 데있어 까다로운 부분은 이미 인증을받은 경우 (예 : 이전 세션 후 페이지를 방문하고 쿠키에 인증 토큰을 저장했거나 페이지를 강제로 새로 고쳤거나, 링크에서 URL로 삭제). ui-router
작동 방식으로 인해 인증 확인 전에 신원 확인을 한 번 수행해야합니다. resolve
상태 구성 의 옵션을 사용하여이 작업을 수행 할 수 있습니다 . 모든 상태가 상속되는 사이트에 대해 하나의 부모 상태가 있으므로 다른 일이 발생하기 전에 교장을 해결해야합니다.
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
여기 또 다른 문제가 있습니다 ... resolve
한 번만 호출됩니다. 신원 조회에 대한 약속이 완료되면 해결 대리인을 다시 실행하지 않습니다. 따라서 두 가지 위치에서 인증 확인을 수행해야합니다. 한 번은 아이덴티티 약속 해결에 resolve
따라 (이는 앱이 처음로드 될 때를 포함하고 $stateChangeStart
, 해결이 완료된 경우에 한 번) 상태를 탐색 할 때마다 적용됩니다.
좋아, 지금까지 우리는 무엇을 했는가?
- 사용자가 로그인했는지 앱이 언제로드되는지 확인합니다.
- 로그인 한 사용자에 대한 정보를 추적합니다.
- 사용자가 로그인해야하는 상태에 대해 로그인 상태로 리디렉션합니다.
- 액세스 권한이없는 경우 액세스 거부 상태로 리디렉션합니다.
- 로그인이 필요한 경우 사용자를 요청한 원래 상태로 다시 리디렉션하는 메커니즘이 있습니다.
- 사용자를 로그 아웃 할 수 있습니다 (인증 티켓을 관리하는 모든 클라이언트 또는 서버 코드와 연계하여 연결해야 함).
- 우리는 하지 않습니다 로그인 페이지로 다시 그들의 브라우저를 다시로드하거나 링크를 드롭 할 때마다 사용자를 보내야합니다.
여기서 어디로 가나 요? 글쎄, 당신은 로그인이 필요 지역으로 상태를 구성 할 수 있습니다. 당신은 추가 인증 / 권한이 부여 된 사용자를 요구할 수 data
로 roles
(상속을 사용하려는 경우, 또는 이들의 부모)이 상태로. 여기서는 리소스를 관리자로 제한합니다.
.state('restricted', {
parent: 'site',
url: '/restricted',
data: {
roles: ['Admin']
},
views: {
'content@': {
templateUrl: 'restricted.html'
}
}
})
이제 사용자가 라우트에 액세스 할 수있는 상태를 상태별로 제어 할 수 있습니다. 다른 문제가 있습니까? 로그인 여부에 따라보기의 일부만 변경 될 수 있습니까? 문제 없어요. 를 사용 principal.isAuthenticated()
하거나 principal.isInRole()
수많은 방법 중 하나를 사용하면 조건부 서식 또는 요소를 표시 할 수 있습니다.
먼저 principal
컨트롤러 또는 다른 것에 주입 하고 스코프에 고정하여보기에서 쉽게 사용할 수 있도록하십시오.
.scope('HomeCtrl', ['$scope', 'principal',
function($scope, principal)
{
$scope.principal = principal;
});
요소 표시 또는 숨기기
<div ng-show="principal.isAuthenticated()">
I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
I'm not logged in
</div>
기타 등등. 어쨌든 예제 앱에서는 인증되지 않은 사용자가 방문 할 수있는 홈페이지 상태가됩니다. 로그인 또는 가입 상태에 대한 링크가 있거나 해당 양식을 해당 페이지에 내장 할 수 있습니다. 당신에게 맞는 것이 무엇이든.
대시 보드 페이지는 모두 사용자가 로그인하고 User
역할 구성원 이되어야하는 상태에서 상속 될 수 있습니다 . 우리가 논의한 모든 인증 항목은 거기서부터 흐릅니다.
지금까지 게시 된 솔루션은 불필요하게 복잡합니다. 더 간단한 방법이 있습니다. 의 문서는ui-router
듣는 말한다 $locationChangeSuccess
사용 $urlRouter.sync()
상태 변화를 확인하기 위해, 그것을 중단하거나 재개. 그러나 그조차도 실제로 작동하지 않습니다.
그러나 여기에 두 가지 간단한 대안이 있습니다. 하나를 선택:
해결 방법 1 : 듣기 $locationChangeSuccess
당신은들을 수 $locationChangeSuccess
있고 어떤 로직, 심지어 비동기 로직을 수행 할 수 있습니다. 해당 논리를 기반으로 함수가 정의되지 않은 상태로 리턴되도록하여 상태 전이가 정상적으로 계속되도록하거나 $state.go('logInPage')
사용자를 인증해야하는 경우 수행 할 수 있습니다 . 예를 들면 다음과 같습니다.
angular.module('App', ['ui.router'])
// In the run phase of your Angular application
.run(function($rootScope, user, $state) {
// Listen to '$locationChangeSuccess', not '$stateChangeStart'
$rootScope.$on('$locationChangeSuccess', function() {
user
.logIn()
.catch(function() {
// log-in promise failed. Redirect to log-in page.
$state.go('logInPage')
})
})
})
이렇게해도 실제로 대상 상태가로드되지는 않지만 사용자가 권한이없는 경우 로그인 페이지로 리디렉션됩니다. 어쨌든 진정한 보호 기능이 서버에 있기 때문에 괜찮습니다.
해결 방법 2 : 상태 사용 resolve
이 솔루션에서는 ui-router
resolve feature 를 사용 합니다 .
기본적으로 resolve
사용자가 인증되지 않은 경우 약속을 거부 한 다음 로그인 페이지로 리디렉션합니다.
방법은 다음과 같습니다.
angular.module('App', ['ui.router'])
.config(
function($stateProvider) {
$stateProvider
.state('logInPage', {
url: '/logInPage',
templateUrl: 'sections/logInPage.html',
controller: 'logInPageCtrl',
})
.state('myProtectedContent', {
url: '/myProtectedContent',
templateUrl: 'sections/myProtectedContent.html',
controller: 'myProtectedContentCtrl',
resolve: { authenticate: authenticate }
})
.state('alsoProtectedContent', {
url: '/alsoProtectedContent',
templateUrl: 'sections/alsoProtectedContent.html',
controller: 'alsoProtectedContentCtrl',
resolve: { authenticate: authenticate }
})
function authenticate($q, user, $state, $timeout) {
if (user.isAuthenticated()) {
// Resolve the promise successfully
return $q.when()
} else {
// The next bit of code is asynchronously tricky.
$timeout(function() {
// This code runs after the authentication promise has been rejected.
// Go to the log-in page
$state.go('logInPage')
})
// Reject the authentication promise to prevent the state from loading
return $q.reject()
}
}
}
)
첫 번째 솔루션과 달리이 솔루션은 실제로 대상 상태가로드되지 않도록합니다.
가장 쉬운 해결책은 사용자가 인증되지 않은 경우 상태 변경 을 사용 $stateChangeStart
하고 event.preventDefault()
취소 하여 로그인 페이지 인 인증 상태로 리디렉션 하는 것입니다.
angular
.module('myApp', [
'ui.router',
])
.run(['$rootScope', 'User', '$state',
function ($rootScope, User, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name !== 'auth' && !User.authenticaded()) {
event.preventDefault();
$state.go('auth');
}
});
}]
);
service
인증 프로세스 (및 저장소)를 처리 하는 것이 필요하다고 생각합니다 .
이 서비스에는 몇 가지 기본 방법이 필요합니다.
isAuthenticated()
login()
logout()
- 등 ...
이 서비스는 각 모듈의 컨트롤러에 주입해야합니다.
- 대시 보드 섹션에서이 서비스를 사용하여 사용자가 인증되었는지 확인하십시오 (
service.isAuthenticated()
메소드). 그렇지 않은 경우 / login으로 리디렉션 - 로그인 섹션에서 양식 데이터를 사용하여
service.login()
메소드를 통해 사용자를 인증하십시오.
이 동작에 대한 강력하고 강력한 예는 프로젝트 angular-app 및 특히 멋진 HTTP Auth 인터셉터 모듈을 기반으로하는 보안 모듈 입니다
도움이 되었기를 바랍니다
이 프로세스를 케이크 조각으로 만들기 위해이 모듈을 만들었습니다.
다음과 같은 작업을 수행 할 수 있습니다.
$routeProvider
.state('secret',
{
...
permissions: {
only: ['admin', 'god']
}
});
아니면
$routeProvider
.state('userpanel',
{
...
permissions: {
except: ['not-logged-in']
}
});
새롭지 만 체크 아웃 할 가치가 있습니다!
https://github.com/Narzerus/angular-permission
ui router 1.0.0.X로 작업하는 다른 솔루션을 공유하고 싶었습니다.
아시다시피 stateChangeStart 및 stateChangeSuccess는 더 이상 사용되지 않습니다. https://github.com/angular-ui/ui-router/issues/2655
대신 $ transitions http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html 을 사용해야합니다.
이것이 내가 달성 한 방법입니다.
먼저 유용한 기능이있는 AuthService 가 있습니다.
angular.module('myApp')
.factory('AuthService',
['$http', '$cookies', '$rootScope',
function ($http, $cookies, $rootScope) {
var service = {};
// Authenticates throug a rest service
service.authenticate = function (username, password, callback) {
$http.post('api/login', {username: username, password: password})
.success(function (response) {
callback(response);
});
};
// Creates a cookie and set the Authorization header
service.setCredentials = function (response) {
$rootScope.globals = response.token;
$http.defaults.headers.common['Authorization'] = 'Bearer ' + response.token;
$cookies.put('globals', $rootScope.globals);
};
// Checks if it's authenticated
service.isAuthenticated = function() {
return !($cookies.get('globals') === undefined);
};
// Clear credentials when logout
service.clearCredentials = function () {
$rootScope.globals = undefined;
$cookies.remove('globals');
$http.defaults.headers.common.Authorization = 'Bearer ';
};
return service;
}]);
그런 다음이 구성이 있습니다.
angular.module('myApp', [
'ui.router',
'ngCookies'
])
.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/resumen');
$stateProvider
.state("dashboard", {
url: "/dashboard",
templateUrl: "partials/dashboard.html",
controller: "dashCtrl",
data: {
authRequired: true
}
})
.state("login", {
url: "/login",
templateUrl: "partials/login.html",
controller: "loginController"
})
}])
.run(['$rootScope', '$transitions', '$state', '$cookies', '$http', 'AuthService',
function ($rootScope, $transitions, $state, $cookies, $http, AuthService) {
// keep user logged in after page refresh
$rootScope.globals = $cookies.get('globals') || {};
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $rootScope.globals;
$transitions.onStart({
to: function (state) {
return state.data != null && state.data.authRequired === true;
}
}, function () {
if (!AuthService.isAuthenticated()) {
return $state.target("login");
}
});
}]);
내가 사용하는 것을 볼 수 있습니다
data: {
authRequired: true
}
인증 된 경우에만 액세스 가능한 상태로 표시합니다.
그런 다음 .run 에서 전환을 사용하여 인증 된 상태를 확인합니다.
$transitions.onStart({
to: function (state) {
return state.data != null && state.data.authRequired === true;
}
}, function () {
if (!AuthService.isAuthenticated()) {
return $state.target("login");
}
});
$ transitions 문서에서 찾은 일부 코드를 사용하여이 예제를 빌드합니다. 나는 ui 라우터에 익숙하지 않지만 작동합니다.
그것이 누군가를 도울 수 있기를 바랍니다.
다음은 우리가 무한 라우팅 루프에서 빠져 나와 $state.go
대신에 어떻게 사용되는지 입니다.$location.path
if('401' !== toState.name) {
if (principal.isIdentityResolved()) authorization.authorize();
}
또 다른 해결책이 있습니다.이 솔루션은 로그인 할 때 표시 할 컨텐츠 만있는 경우 완벽하게 작동합니다. 로그인했는지 여부와 화이트리스트 경로 경로가 아닌지 확인할 규칙을 정의하십시오.
$urlRouterProvider.rule(function ($injector, $location) {
var UserService = $injector.get('UserService');
var path = $location.path(), normalized = path.toLowerCase();
if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
$location.path('/login/signin');
}
});
내 예에서 나는 로그인하지 않았는지 묻고 라우팅하려는 현재 경로가`/ login '의 일부가 아닙니다.
/login/signup // registering new user
/login/signin // login to app
그래서 나는이 두 경로에 즉시 액세스 할 수 있으며 온라인 상태이면 다른 모든 경로가 확인됩니다.
다음은 로그인 모듈에 대한 전체 라우팅 파일입니다.
export default (
$stateProvider,
$locationProvider,
$urlRouterProvider
) => {
$stateProvider.state('login', {
parent: 'app',
url: '/login',
abstract: true,
template: '<ui-view></ui-view>'
})
$stateProvider.state('signin', {
parent: 'login',
url: '/signin',
template: '<login-signin-directive></login-signin-directive>'
});
$stateProvider.state('lock', {
parent: 'login',
url: '/lock',
template: '<login-lock-directive></login-lock-directive>'
});
$stateProvider.state('signup', {
parent: 'login',
url: '/signup',
template: '<login-signup-directive></login-signup-directive>'
});
$urlRouterProvider.rule(function ($injector, $location) {
var UserService = $injector.get('UserService');
var path = $location.path();
if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
$location.path('/login/signin');
}
});
$urlRouterProvider.otherwise('/error/not-found');
}
() => { /* code */ }
ES6 구문입니다. 대신 사용하십시오. function() { /* code */ }
먼저 앱 인증 상태에 대한 아이디어가있는 컨트롤러에 주입 할 수있는 서비스가 필요합니다. 로컬 저장소로 인증 세부 정보를 유지하는 것은 적절한 접근 방법입니다.
다음으로 상태가 변경되기 직전에 인증 상태를 확인해야합니다. 앱에는 인증이 필요한 일부 페이지와 그렇지 않은 페이지가 있으므로 인증을 확인하는 상위 경로를 생성하고 동일한 다른 모든 페이지를 해당 상위의 하위로 만듭니다.
마지막으로 현재 로그인 한 사용자가 특정 작업을 수행 할 수 있는지 알 수있는 방법이 필요합니다. 인증 서비스에 'can'기능을 추가하면됩니다. -조치-필수-(예 : 'manage_dashboards'또는 'create_new_dashboard')-오브젝트-선택적-조작중인 오브젝트의 두 매개 변수를 사용할 수 있습니다. 예를 들어 대시 보드 개체가있는 경우 dashboard.ownerId === logsInUser.id인지 확인할 수 있습니다. 물론 클라이언트에서 전달 된 정보는 절대 신뢰할 수 없으며 데이터베이스에 쓰기 전에 항상 서버에서이를 확인해야합니다.
angular.module('myApp', ['ngStorage']).config([
'$stateProvider',
function(
$stateProvider
) {
$stateProvider
.state('home', {...}) //not authed
.state('sign-up', {...}) //not authed
.state('login', {...}) //not authed
.state('authed', {...}) //authed, make all authed states children
.state('authed.dashboard', {...})
}])
.service('context', [
'$localStorage',
function(
$localStorage
) {
var _user = $localStorage.get('user');
return {
getUser: function() {
return _user;
},
authed: function() {
return (_user !== null);
},
// server should return some kind of token so the app
// can continue to load authenticated content without having to
// re-authenticate each time
login: function() {
return $http.post('/login.json').then(function(reply) {
if (reply.authenticated === true) {
$localStorage.set(_userKey, reply.user);
}
});
},
// this request should expire that token, rendering it useless
// for requests outside of this session
logout: function() {
return $http.post('logout.json').then(function(reply) {
if (reply.authenticated === true) {
$localStorage.set(_userKey, reply.user);
}
});
},
can: function(action, object) {
if (!this.authed()) {
return false;
}
var user = this.getUser();
if (user && user.type === 'admin') {
return true;
}
switch(action) {
case 'manage_dashboards':
return (user.type === 'manager');
}
return false;
}
}
}])
.controller('AuthCtrl', [
'context',
'$scope',
function(
context,
$scope
) {
$scope.$root.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
//only require auth if we're moving to another authed page
if (toState && toState.name.indexOf('authed') > -1) {
requireAuth();
}
});
function requireAuth() {
if (!context.authed()) {
$state.go('login');
}
}
}]
** 면책 조항 : 위 코드는 의사 코드이며 보증이 없습니다 **
$ http 인터셉터 사용
$ http 인터셉터를 사용하면 백엔드 또는 다른 방법으로 헤더를 보내고 그 방법으로 검사를 수행 할 수 있습니다.
$ http 인터셉터 에 관한 훌륭한 기사
예:
$httpProvider.interceptors.push(function ($q) {
return {
'response': function (response) {
// TODO Create check for user authentication. With every request send "headers" or do some other check
return response;
},
'responseError': function (reject) {
// Forbidden
if(reject.status == 403) {
console.log('This page is forbidden.');
window.location = '/';
// Unauthorized
} else if(reject.status == 401) {
console.log("You're not authorized to view this page.");
window.location = '/';
}
return $q.reject(reject);
}
};
});
이것을 .config 또는 .run 함수에 넣으십시오.
참고 URL : https://stackoverflow.com/questions/22537311/angularjs-ui-router-login-authentication
'Programming' 카테고리의 다른 글
Python으로 작성된 CSV 파일에는 각 행 사이에 빈 줄이 있습니다. (0) | 2020.02.26 |
---|---|
오리진의 마스터 브랜치 파일 버전으로 되 돌리는 방법 (0) | 2020.02.26 |
Git 브랜치의 가장 가까운 부모를 찾는 방법은 무엇입니까? (0) | 2020.02.26 |
Ruby 클래스의 이름은 어떻게 얻습니까? (0) | 2020.02.26 |
Internet Explorer에서 'console'은 정의되지 않은 오류입니다. (0) | 2020.02.26 |