나는 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']
}
});
새롭지 만 체크 아웃 할 가치가 있습니다!
답변
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();
}