私はAngularJSに慣れていないので、次のような状況でAngularJiをどのように使用できるかについて少し混乱しています。
私は2つのセクションからなるWebアプリケーションを構築しています。最初のセクションはログインビューとサインアップビューのあるホームページで、2番目のセクションはダッシュボードです(ログイン成功後)。
私はホームセクション用のindex.html
をそのangle appとui-router
および/login
ビューを扱うための/signup
configで作成しました。また、ダッシュボードセクション用の別のdashboard.html
というアプリとui-router
configで多くのサブビューを処理します。
今、私はダッシュボードセクションを終えました、そして、2つのセクションを彼らの異なった角度のあるアプリと組み合わせる方法を知りません。ホームアプリにダッシュボードアプリにリダイレクトするように指示するにはどうすればよいですか。
私の意見では、これまでに投稿された解決策は不必要に複雑です。もっと簡単な方法があります。 ui-router
のドキュメント は$locationChangeSuccess
を監視し、$urlRouter.sync()
を使って状態遷移の確認、停止、再開を行います。しかし、それでも実際には機能しません。
ただし、これには2つの簡単な方法があります。一つを選ぶ:
$locationChangeSuccess
をリッスンするあなたは$locationChangeSuccess
を聞くことができ、そこでは非同期ロジックでさえも何らかのロジックを実行することができます。そのロジックに基づいて、関数にundefinedを返させることができます。これにより、状態遷移を通常どおり継続させることができます。あるいは、ユーザーを認証する必要がある場合は$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')
})
})
})
これは実際にはターゲット状態の読み込みを妨げるものではありませんが、ユーザーが承認されていない場合はログインページにリダイレクトされます。とにかく、本当の保護がサーバーにあるのでそれは大丈夫です。
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()
を使用して状態の変更をキャンセルし、ログインページである auth 状態にリダイレクトすることです。
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()
メソッドを通してユーザーを認証するためにフォームデータを使うだけですこの振る舞いの良い頑強な例はproject angular-app そして特に securityモジュール です。これは素晴らしい HTTP Auth Interceptor Module を基にしています。
お役に立てれば
このプロセスを簡単にするためにこのモジュールを作成しました
次のようなことができます。
$routeProvider
.state('secret',
{
...
permissions: {
only: ['admin', 'god']
}
});
または
$routeProvider
.state('userpanel',
{
...
permissions: {
except: ['not-logged-in']
}
});
新品ですが、チェックする価値があります。
私は、UIルーター1.0.0.Xで動作する別の解決策を共有したいと思いました
ご存じのとおり、stateChangeStartとstateChangeSuccessは現在は推奨されていません。 https://github.com/angular-ui/ui-router/issues/2655
代わりに、$ transition 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");
}
});
この例は、$ transitionのドキュメントにあるコードを使って作成します。私は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
だから私はこの2つのルートへの即時アクセスを持っており、あなたがオンラインであれば他のすべてのルートがチェックされます。
これはログインモジュールのための私の全体のルーティングファイルです。
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 */ }
を使用してください。
まず、アプリの認証状態について何らかのアイデアを持った、コントローラーに注入できるサービスが必要です。ローカルストレージで認証情報の詳細を保持することは、それに取り組むための適切な方法です。
次に、状態が変わる直前にauthの状態を確認する必要があります。認証される必要があるページと認証されないページがあるので、authをチェックする親ルートを作成し、それを必要とする他のすべてのページをその親の子にします。
最後に、現在ログインしているユーザーが特定の操作を実行できるかどうかを判断するための何らかの方法が必要になります。これはあなたの認証サービスに 'can'関数を追加することで達成できます。次の2つのパラメータを取ることができます。 - action - required - (つまり、「manage_dashboards」または「create_new_dashboard」) - object - オプション - 操作されているオブジェクト。たとえば、ダッシュボードオブジェクトがある場合は、dashboard.ownerId ===loggedInUser.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インターセプターを使用することで、バックエンドまたは他の方法でヘッダーを送信し、その方法でチェックを行うことができます。
例:
$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関数に入れてください。