LINUX.ORG.RU

Авторизация в AngularJS приложении

 , , ,


0

1

Я уже поднимал этот вопрос тут. Но есть еще пару моментов над которыми я еще ломаю голову, в плане правильной реализации. Еще раз опишу структуру приложения. Есть отдельно api и отдельно front-end где за все отвечает полностью angular, то есть за формирование routes и т.д. С сервером он общается для запроса того или иного набора данных. Вот нужно написать авторизацию для этой связки. Естественно есть страницы которые может видеть одна группа пользователей, а другая нет, ну и на оборот. Для routes использую ngRoute. Естественно с коробки не существует в angular проверки прав пользователей и это надо писать самому. Перечитал ряд статей и реализовал посредством cookie,но есть одно но. Теперь хочу написать механизм который будет при каждом изменении роута смотреть какие группы пользователей могут его видеть и отдавать или нет нужную страницу. Есть мысль написать это на back-end в виде функции check которая будет проверять можно ли юзеру смотреть страницу или нет, и уже отдавать какой-то ответ, но если это делать при каждом изменении страницы, то я думаю это будет лишняя нагрузка на сервер, плюс в то же время надо для каждого авторизованного пользователя в правом верхнем углу отображать его логин и еще ряд информации , можно и как-то реализовать сервис который для каждого пользователя в rootScope будет создать объект user, к примеру, и давать ему определенные свойства и для каждого пользователя это будет свой объект, просто это поможет избежать лишних запросов на back-end и в механизме проверять объект с rootScope что будет явно быстрее и не будет создавать такую нагрузку на сервер. Но я пока не вижу возможности реализации уникального объекта user для каждого пользователя, ведь это не сессии в конце концов которые можно создавать на стороне сервера и они дают уникальность для каждого клиента. Можете скажем так оценить направление идеи или может подсказать что-то более рациональные ну и поправить меня где нужно?


Бэкэнд может выдать список всех разрешений? Если нет, замени его на вменяемый.

Фронтэнд может сделать мап вида страница→разрешение (и элемент страницы→разрешение если, например, на редактирование оно отдельно и нужно скрывать/отключать его в гуе) и сопоставить с выданным бэкэндом списком (который можно получать один раз)? В чём проблема?

x3al ★★★★★ ()
Ответ на: комментарий от x3al

Один раз могу запросить еще где-то этапе входа пользователя в систему но вот дальше как? Я пробовал получить доступ к тем cookie что приложение оправляет при каждом запросе к серверу но это мне не удалось. Можно хранить в localStorage но там будут храниться все состояния пользователей и не ясно как выбрать нужные.

Berdin ()

Есть мысль написать это на back-end в виде функции check которая будет проверять можно ли юзеру смотреть страницу или нет,

На бэкэнде вообще никаких проверок нет что-ли? О_о.

Куки кстати далеко не лучший выбор. Лучше использовать токены. https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-to...

По случайному совпадению сейчас тоже вожусь с аутентификацией и авторизацией. Использую таки куки :3

В run есть что-то вроде этого:

$rootScope.$on("$routeChangeStart", function (event, next) {
    next = next.hasOwnProperty('$$route') ? next['$$route']['name'] : false;
    rpc.call('user.is.authorized').then(
        function (response) {
            var isAuthorized = response['is_authorized'];
            var roles = response['roles'];
            if (angular.isString(next)) {
                var redirect = false;
                if (isAuthorized) {
                    redirect = ['login', 'register'].indexOf(next) !== -1;
                } else {
                    redirect = next == 'logout';
                }
                if (redirect) {
                    $route.redirect('error');
                }
            }
            var defend;
            for (var navItem in navList) {
                if (navList[navItem].hasOwnProperty('defend')) {
                    defend = navList[navItem].defend;
                    navigation.display(navItem, isAuthorized ? defend : !defend)
                }
            }
            navigation.display('users', roles.indexOf('ROLE_ADMIN') !== -1);
        }
    );
});
В идеале, лучше не делать rpc.call('user.is.authorized') при каждом изменении роута, а хранить инфу где-то в памяти. Сервис под это вполне можно организовать. При первичном обращении к атрибуту сервиса делать запрос на сервер, дальше использовать везде полученные значения. Ну и проверки тоже стоит куда-то в другое место вынести.

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

А это даст уникальность данных для каждого пользователя? Чтобы не было аврала данных с которых не понятно которые твои.

Berdin ()
Ответ на: комментарий от Berdin

Не вижу проблем. На бэкэнде всё равно проверяются права доступа, ведь так же? А на фронтенде ты просто хранишь в памяти инфу о текущем юзере. Куки будут слаться с каждым запросом к бэкэнду и без тебя. Тебе нужно только знать текущее состояние юзера, чтобы определять, что показывать на фронтенде.

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

Они будут проверяться, вот я просто озадачен тем что проверять права доступа при каждом изменении route будет очень напряжно в плане каждый раз слать запрос к серверу на проверку доступа. А вот если раз отправить и потом в локальное хранилище положить какие-то маячки для проверки доступа и не ломиться потом на сервер. Но меня волнует вопрос - к примеру я ложу все это в localStorage для каждого пользователя оно будет свое или там грубо говоря будут собираться данные всех пользователей и не понятно какие именно нужные мне?

Berdin ()
Ответ на: комментарий от Berdin

/api/whatever/whoamiandwhataremypermission, возвращающий что угодно. Хранить — да чем угодно, хоть сервис (в терминах angular) делай, кэширующий это. Нафиг кукизы/localstorage?

x3al ★★★★★ ()
Ответ на: комментарий от Berdin

localStorage уникален для каждого юзера. Только зачем туда что-то ложить, когда можно сохранить это в памяти? Так состояние юзера будет запрашиваться у сервера при каждом открытии страницы. Не при смене роута, а только при первом обращении к объекту.

С локальным хранилищем тебе скорее всего придётся обрабатывать ситуацию, когда срок действия токена истёк, но он всё еще находится в локальном хранилище. Вобщем оно всё только усложнит, а польза сомнительна. ИМХО, конечно же.

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

Честно может не до понял, что именно ты имеешь в виду по памятью в данном контексте? Что-то типа Redis, о какой памяти тут идет речь?

Berdin ()
Ответ на: комментарий от Berdin

Та, что выделяется под переменную.

Как-то так это будет выглядеть наверное:

module.factory(
    'auth',
    function ($q, rpc) {
        var userInfo = null;
        return {
            info: function () {
                var deferred = $q.defer(); 
                if (userInfo == null) {
                    rpc.call('user.info').then(
                        function (result) {
                            userInfo = result;
                            deferred.resolve(result);
                        },
                        function (error) {
                            deferred.reject(error);
                        }
                    );
                } else {
                    deferred.resolve(userInfo);
                }
                return deferred.promise;
            }
        }
    }
);

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

Ок, буду пытаться реализовывать для себя это все, эта factory даст уникальные данные для каждого пользователя выходит?

Berdin ()
Ответ на: комментарий от Berdin

А почему нет? Откуда вообще такие опасения? Идёт запрос на бэкэнд с куками или без, которые были установлены у этого юзера. В переменной userInfo сохраняется ответ от бэкэнда. Как туда данные другого юзера могут попасть? Я не представляю. Даже если что-то и пойдёт не так, то бэкэнд не позволит ничего сломать.

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

Ок, буду разбираться, если где-то что-то не пойму правильно, буду этот топик продолжать.

Berdin ()
Ответ на: комментарий от Kilte

Выходит эта factory инжектится в каждом controller-е где нужна user info?А вот еще вопрос назрел

rpc.call('user.is.authorized').then(
        function (response) {
            var isAuthorized = response['is_authorized'];
            var roles = response['roles'];
Как я понял ты все таки на каждом изменении страницы этим кодом запрашиваешь с сервера данные о том авторизован ползователь или нет, или я не уловил смысл той единой точки проверки авторизации которая потом используется во всем приложении без множественных запросов к серверу.

Berdin ()
Ответ на: комментарий от Kilte

И вновь я) вот ссылка на небольшой кусок моего кода, есть одна проблема которую пока не удалось решить http://code.re/77d если что строго код не суди, он пока прототипный в плане что только начинает описывать концепцию общую, сама проблема вот в чем, написал я сервис auth и в обработчике события $routeChangeStart делаю необходимую проверку основываясь уже на установленном cookie при логине, но вот в чем незадача, я прошел процедуру логина, и дальше в шаблонах вывожу имя и логин юзера, но сразу после логина он не выводиться, как только перезагружаю страницу то в шаблонах появляется нужная инфа, как я понимаю при перезагрузке страницы происходит boostrap самого приложения, я на этапе 200 ответа при логиче хотел в rootScope записать эти нужные значения, а там дальше уже за них отвечал бы сервис auth, вопрос в том как правильно это сделать, что-то в роде rootScope.reload чтобы уже сразу после логина нужные данные попадали в шаблоны.

Berdin ()
Ответ на: комментарий от Berdin

и дальше в шаблонах вывожу имя и логин юзера, но сразу после логина он не выводиться

И не будет, потому что run выполняется только один раз.

Можно сделать директиву и этого должно быть достаточно. Нужно только не забывать обновлять userInfo.

Ещё один вариант — через события. Опять же в $rootScope подписываешься на какой-нибудь auth и обновляешь данные. При логине/выходе генерируешь событие auth и всё. Только что-то мне подсказывает, что так делать не надо. В $rootScope лучше вообще ничего не хранить и завязывать сервисы/контроллеры на него тоже не стоит. ИМХО.

Kilte ★★★★★ ()
Ответ на: комментарий от Berdin

Забудь про директиву. Ха-ха. У нас же только обещание можно вернуть и при изменении данных юзера как ни крути придётся юзать эвенты. Могу ошибаться конечно.

Kilte ★★★★★ ()
Ответ на: комментарий от Kilte

С эвентами возился но не получилось, попробую еще раз только немного другим способом. А в фоне значит - пришел ответ с сервера что пользователь авторизован, ну и я перед редиректом на главную делаю re-boostrap приложения, что могло бы дать нужный результат, но как это сделать и возможно ли это - я не знаю.

Berdin ()
Ответ на: комментарий от Kilte

Но плюс в моей архитектуре есть еще одна тонкость - страницы есть как публичные так и доступные только одной из n-групп пользователей и я теперь в замешательстве где же все таки эту проверку цеплять.

Berdin ()
Ответ на: комментарий от Berdin

При смене роута, где же ещё. Можешь сделать сервис, который будет отвечать за проверки и вызывать метод, который определяет доступ при этом событии. Чтобы run сильно не перегружать.

Kilte ★★★★★ ()
Ответ на: комментарий от Berdin

где же все таки эту проверку цеплять.

В параметре resolve внутри определения route.

risenshnobel ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.