programing

AngularJS 인증 + RESTful API

magicmemo 2023. 3. 2. 22:09
반응형

AngularJS 인증 + RESTful API

Auth/(re) 루팅을 위한 API를 사용한 Angular+RESTFUL 클라이언트 측 통신

이 내용은 몇 가지 다른 질문과 튜토리얼에서 다루어졌지만, 지금까지 접한 모든 리소스는 정곡을 찌르지 못했습니다.

한마디로 말하면,

  • POST에서 http://client.foo로로 합니다.http://api.foo/login
  • 「의 GUI/, 「로그인」의 /컴포넌트 스테이트가 「 GUI/컴포넌트는, 「로그인」의 GUI/컴포넌트 상태가 .logout 변경
  • 사용자가 로그아웃/로그아웃할 때 UI를 "업데이트"할 수 있습니다.이게 제일 답답했어
  • 루트를 보호하여 authenticated-state를 확인하고(필요에 따라) 사용자를 로그인 페이지로 리다이렉트합니다.

저의 고민은

  • 마다 에 합니다.api.foo/status사용자가 로그인하고 있는지 여부를 판단합니다.(루트에 Express를 사용하고 있습니다.은 Angular가 Angular로 .ng-show="user.is_authenticated"
  • 했을 때 (이것을).「/」「/」「/」등의 할 수 .{{user.first_name}}또는 로그아웃의 경우는, 그 값을 비웁니다.
// Sample response from `/status` if successful 

{
   customer: {...},
   is_authenticated: true,
   authentication_timeout: 1376959033,
   ...
}

내가 시도한 것

왜 내가 제정신이 아닌 것 같은지

  • 모든 튜토리얼은 일부 데이터베이스(Mongo, Couch, PHP+MySQL, adinitum) 솔루션에 의존하며 로그인 상태를 유지하기 위해 RESTful API와의 통신에만 의존하지 않습니다. 「」, 「/GET」와 함께 의 POST/GET 가 됩니다.withCredentials:true 그게
  • Angular+REST+Auth를 실행하고 백엔드 언어를 사용하는 예/튜토리얼/리포트를 찾을 수 없습니다.

난 별로 자랑스럽지 않아

솔직히 말해서, 저는 Angular에 처음이고, 제가 이 문제에 대해 터무니없이 접근하더라도 놀라지 않을 것입니다. 누군가 다른 대안을 제시해 준다면 정말 기쁠 것입니다.

하고 Express가 정말 하기 때문에Jade ★★★★★★★★★★★★★★★★★」Stylus. - 결혼하지 .Express은 Angular의 앵귤러

아무나 할 수 있는 도움에 미리 감사드립니다.그리고 구글에 부탁하지 마세요.저는 26페이지 정도의 보라색 링크가 있습니다.;-)


1이 솔루션은 Angular의 $httpBackend 모의에 의존하며 실제 서버와 통신하는 방법은 불분명합니다.

2이것이 가장 가까웠지만, 인증이 필요한 기존 API가 있기 때문에 패스포트의 'localStrategy'를 사용할 수 없고, OAUTH 서비스를 작성하는 것은 미친 것 같았습니다.나 혼자만 쓰려고 했던 거야

이것은 url 루트 허가 및 요소 보안에 관한 블로그 투고에서 가져온 것입니다만, 요점을 간단하게 정리하겠습니다.-)

프런트엔드 웹 어플리케이션의 보안은 Joe Public을 저지하기 위한 시작 수단일 뿐이지만 웹 지식이 있는 사용자는 이를 회피할 수 있으므로 보안 서버 측도 항상 확보해야 합니다.

각진 보안에 관한 주요 관심사는 경로 보안입니다.각진 경로를 정의할 때 다른 속성을 가질 수 있는 객체, 즉 객체를 만듭니다.이 접근법의 초석은 보안 객체를 이 루트 오브젝트에 추가하는 것입니다.이 루트 오브젝트는 기본적으로 사용자가 특정 루트에 액세스하기 위해 필요한 역할을 정의합니다.

 // route which requires the user to be logged in and have the 'Admin' or 'UserManager' permission
    $routeProvider.when('/admin/users', {
        controller: 'userListCtrl',
        templateUrl: 'js/modules/admin/html/users.tmpl.html',
        access: {
            requiresLogin: true,
            requiredPermissions: ['Admin', 'UserManager'],
            permissionType: 'AtLeastOne'
        });

전체 접근 방식은 기본적으로 사용자에게 필요한 권한이 있는지 확인하는 인증 서비스를 중심으로 합니다.이 서비스는, 이 솔루션의 다른 부분으로부터, 유저에 관한 염려와 로그인시에 서버로부터 취득했을 실제의 권한을 추상화합니다.그 코드는 꽤 장황하지만 내 블로그 포스트에 충분히 설명되어 있다.그러나 기본적으로 허가 확인과 두 가지 허가 모드를 처리합니다.첫 번째는 사용자가 정의된 권한 중 적어도1개를 가지고 있어야 하고 두 번째는 정의된 모든 권한을 가지고 있어야 합니다.

angular.module(jcs.modules.auth.name).factory(jcs.modules.auth.services.authorization, [  
'authentication',  
function (authentication) {  
 var authorize = function (loginRequired, requiredPermissions, permissionCheckType) {
    var result = jcs.modules.auth.enums.authorised.authorised,
        user = authentication.getCurrentLoginUser(),
        loweredPermissions = [],
        hasPermission = true,
        permission, i;

    permissionCheckType = permissionCheckType || jcs.modules.auth.enums.permissionCheckType.atLeastOne;
    if (loginRequired === true && user === undefined) {
        result = jcs.modules.auth.enums.authorised.loginRequired;
    } else if ((loginRequired === true && user !== undefined) &&
        (requiredPermissions === undefined || requiredPermissions.length === 0)) {
        // Login is required but no specific permissions are specified.
        result = jcs.modules.auth.enums.authorised.authorised;
    } else if (requiredPermissions) {
        loweredPermissions = [];
        angular.forEach(user.permissions, function (permission) {
            loweredPermissions.push(permission.toLowerCase());
        });

        for (i = 0; i < requiredPermissions.length; i += 1) {
            permission = requiredPermissions[i].toLowerCase();

            if (permissionCheckType === jcs.modules.auth.enums.permissionCheckType.combinationRequired) {
                hasPermission = hasPermission && loweredPermissions.indexOf(permission) > -1;
                // if all the permissions are required and hasPermission is false there is no point carrying on
                if (hasPermission === false) {
                    break;
                }
            } else if (permissionCheckType === jcs.modules.auth.enums.permissionCheckType.atLeastOne) {
                hasPermission = loweredPermissions.indexOf(permission) > -1;
                // if we only need one of the permissions and we have it there is no point carrying on
                if (hasPermission) {
                    break;
                }
            }
        }

        result = hasPermission ?
                 jcs.modules.auth.enums.authorised.authorised :
                 jcs.modules.auth.enums.authorised.notAuthorised;
    }

    return result;
};

루트에 보안이 설정되어 있기 때문에 루트 변경이 시작되었을 때 사용자가 루트에 액세스할 수 있는지 여부를 판단할 수 있는 방법이 필요합니다.이를 위해 루트 변경 요구를 대행 수신하고 루트 오브젝트(새로운 액세스오브젝트가 있는 경우)를 검사하여 사용자가 뷰에 액세스할 수 없는 경우 다른 루트로 교체합니다.

angular.module(jcs.modules.auth.name).run([  
    '$rootScope',
    '$location',
    jcs.modules.auth.services.authorization,
    function ($rootScope, $location, authorization) {
        $rootScope.$on('$routeChangeStart', function (event, next) {
            var authorised;
            if (next.access !== undefined) {
                authorised = authorization.authorize(next.access.loginRequired,
                                                     next.access.permissions,
                                                     next.access.permissionCheckType);
                if (authorised === jcs.modules.auth.enums.authorised.loginRequired) {
                    $location.path(jcs.modules.auth.routes.login);
                } else if (authorised === jcs.modules.auth.enums.authorised.notAuthorised) {
                    $location.path(jcs.modules.auth.routes.notAuthorised).replace();
                }
            }
        });
    }]);

여기서 키는 실제로는 '.replace'입니다.이는 현재 루트(표시 권한이 없는 루트)를 리다이렉트하는 루트로 치환하기 때문입니다.이 정류장은 허가되지 않은 경로로 되돌아갑니다.

이제 사용자가 로그인해야 하는 경로에 도달한 경우 로그인리다이렉트하는 등 멋진 루트를 대행 수신할 수 있습니다.

솔루션의 두 번째 부분은 권한에 따라 사용자에게 UI 요소를 숨기거나 표시할 수 있는 것입니다.이것은 간단한 지시를 통해 달성된다.

angular.module(jcs.modules.auth.name).directive('access', [  
        jcs.modules.auth.services.authorization,
        function (authorization) {
            return {
              restrict: 'A',
              link: function (scope, element, attrs) {
                  var makeVisible = function () {
                          element.removeClass('hidden');
                      },
                      makeHidden = function () {
                          element.addClass('hidden');
                      },
                      determineVisibility = function (resetFirst) {
                          var result;
                          if (resetFirst) {
                              makeVisible();
                          }

                          result = authorization.authorize(true, roles, attrs.accessPermissionType);
                          if (result === jcs.modules.auth.enums.authorised.authorised) {
                              makeVisible();
                          } else {
                              makeHidden();
                          }
                      },
                      roles = attrs.access.split(',');


                  if (roles.length > 0) {
                      determineVisibility(true);
                  }
              }
            };
        }]);

그런 다음 다음과 같은 요소를 확인할 수 있습니다.

 <button type="button" access="CanEditUser, Admin" access-permission-type="AtLeastOne">Save User</button>

접근법에 대한 자세한 내용은 블로그 투고 전문을 참조하십시오.

애플리케이션 서비스 콜을 수작업으로 만들고 있기 때문에 $리소스를 사용하지 않고 있습니다.즉, 어떤 종류의 초기화 데이터를 얻는 다른 모든 서비스에 의존하는 서비스를 통해 로그인을 처리했습니다.로그인이 성공하면 모든 서비스의 초기화를 트리거합니다.

컨트롤러 범위 내에서 loginServiceInformation을 감시하고 그에 따라 모델의 속성을 입력합니다(적절한 ng-show/hide를 트리거합니다).라우팅에 관해서는 Angular's built-in routing을 사용하고 있으며 단순히 로그에 근거한ng-hide를 가지고 있습니다.여기에 표시된 부울에서는 로그인을 요구하는 텍스트 또는 ng-view 속성을 가진 div가 표시됩니다(따라서 로그인 후 바로 로그인하지 않으면 현재 모든 뷰에 대한 데이터를 로드하지만 필요에 따라 데이터를 더 선택할 수 있습니다).

//Services
angular.module("loginModule.services", ["gardenModule.services",
                                        "surveyModule.services",
                                        "userModule.services",
                                        "cropModule.services"
                                        ]).service(
                                            'loginService',
                                            [   "$http",
                                                "$q",
                                                "gardenService",
                                                "surveyService",
                                                "userService",
                                                "cropService",
                                                function (  $http,
                                                            $q,
                                                            gardenService,
                                                            surveyService,
                                                            userService,
                                                            cropService) {

    var service = {
        loginInformation: {loggedIn:false, username: undefined, loginAttemptFailed:false, loggedInUser: {}, loadingData:false},

        getLoggedInUser:function(username, password)
        {
            service.loginInformation.loadingData = true;
            var deferred = $q.defer();

            $http.get("php/login/getLoggedInUser.php").success(function(data){
                service.loginInformation.loggedIn = true;
                service.loginInformation.loginAttemptFailed = false;
                service.loginInformation.loggedInUser = data;

                gardenService.initialize();
                surveyService.initialize();
                userService.initialize();
                cropService.initialize();

                service.loginInformation.loadingData = false;

                deferred.resolve(data);
            }).error(function(error) {
                service.loginInformation.loggedIn = false;
                deferred.reject(error);
            });

            return deferred.promise;
        },
        login:function(username, password)
        {
            var deferred = $q.defer();

            $http.post("php/login/login.php", {username:username, password:password}).success(function(data){
                service.loginInformation.loggedInUser = data;
                service.loginInformation.loggedIn = true;
                service.loginInformation.loginAttemptFailed = false;

                gardenService.initialize();
                surveyService.initialize();
                userService.initialize();
                cropService.initialize();

                deferred.resolve(data);
            }).error(function(error) {
                service.loginInformation.loggedInUser = {};
                service.loginInformation.loggedIn = false;
                service.loginInformation.loginAttemptFailed = true;
                deferred.reject(error);
            });

            return deferred.promise;
        },
        logout:function()
        {
            var deferred = $q.defer();

            $http.post("php/login/logout.php").then(function(data){
                service.loginInformation.loggedInUser = {};
                service.loginInformation.loggedIn = false;
                deferred.resolve(data);
            }, function(error) {
                service.loginInformation.loggedInUser = {};
                service.loginInformation.loggedIn = false;
                deferred.reject(error);
            });

            return deferred.promise;
        }
    };
    service.getLoggedInUser();
    return service;
}]);

//Controllers
angular.module("loginModule.controllers", ['loginModule.services']).controller("LoginCtrl", ["$scope", "$location", "loginService", function($scope, $location, loginService){

    $scope.loginModel = {
                        loadingData:true,
                        inputUsername: undefined,
                        inputPassword: undefined,
                        curLoginUrl:"partials/login/default.html",
                        loginFailed:false,
                        loginServiceInformation:{}
                        };

    $scope.login = function(username, password) {
        loginService.login(username,password).then(function(data){
            $scope.loginModel.curLoginUrl = "partials/login/logoutButton.html";
        });
    }
    $scope.logout = function(username, password) {
        loginService.logout().then(function(data){
            $scope.loginModel.curLoginUrl = "partials/login/default.html";
            $scope.loginModel.inputPassword = undefined;
            $scope.loginModel.inputUsername = undefined;
            $location.path("home");
        });
    }
    $scope.switchUser = function(username, password) {
        loginService.logout().then(function(data){
            $scope.loginModel.curLoginUrl = "partials/login/loginForm.html";
            $scope.loginModel.inputPassword = undefined;
            $scope.loginModel.inputUsername = undefined;
        });
    }
    $scope.showLoginForm = function() {
        $scope.loginModel.curLoginUrl = "partials/login/loginForm.html";
    }
    $scope.hideLoginForm = function() {
        $scope.loginModel.curLoginUrl = "partials/login/default.html";
    }

    $scope.$watch(function(){return loginService.loginInformation}, function(newVal) {
        $scope.loginModel.loginServiceInformation = newVal;
        if(newVal.loggedIn)
        {
            $scope.loginModel.curLoginUrl = "partials/login/logoutButton.html";
        }
    }, true);
}]);

angular.module("loginModule", ["loginModule.services", "loginModule.controllers"]);

HTML

<div style="height:40px;z-index:200;position:relative">
    <div class="well">
        <form
            ng-submit="login(loginModel.inputUsername, loginModel.inputPassword)">
            <input
                type="text"
                ng-model="loginModel.inputUsername"
                placeholder="Username"/><br/>
            <input
                type="password"
                ng-model="loginModel.inputPassword"
                placeholder="Password"/><br/>
            <button
                class="btn btn-primary">Submit</button>
            <button
                class="btn"
                ng-click="hideLoginForm()">Cancel</button>
        </form>
        <div
            ng-show="loginModel.loginServiceInformation.loginAttemptFailed">
            Login attempt failed
        </div>
    </div>
</div>

위의 부분을 사용하여 그림을 완성하는 기본 HTML:

<body ng-controller="NavigationCtrl" ng-init="initialize()">
        <div id="outerContainer" ng-controller="LoginCtrl">
            <div style="height:20px"></div>
            <ng-include src="'partials/header.html'"></ng-include>
            <div  id="contentRegion">
                <div ng-hide="loginModel.loginServiceInformation.loggedIn">Please login to continue.
                <br/><br/>
                This new version of this site is currently under construction.
                <br/><br/>
                If you need the legacy site and database <a href="legacy/">click here.</a></div>
                <div ng-view ng-show="loginModel.loginServiceInformation.loggedIn"></div>
            </div>
            <div class="clear"></div>
            <ng-include src="'partials/footer.html'"></ng-include>
        </div>
    </body>

로그인 컨트롤러가 DOM 상단의 ng 컨트롤러로 정의되어 있기 때문에 로그인을 기반으로 페이지 본문 영역을 변경할 수 있습니다.가변.

양식 검증은 아직 구현하지 않았습니다.또한 Angular에게는 아직 매우 신선하기 때문에 이 게시물에 대한 어떤 조언도 환영입니다.RESTful 기반 구현이 아니기 때문에 질문에 직접 답할 수는 없지만 $http 호출을 기반으로 구축되어 있기 때문에 $리소스에 적용할 수 있다고 생각합니다.

나는 Angular를 썼다.UserApp용 JS 모듈: 원하는 거의 모든 기능을 제공합니다.다음 중 하나를 수행할 수 있습니다.

  1. 모듈을 수정하고 함수를 자신의 API에 부가합니다.
  2. 모듈을 사용자 관리 API인 UserApp과 함께 사용합니다.

https://github.com/userapp-io/userapp-angular

보호된/퍼블릭루트, 로그인/로그아웃 시 재루팅, 상태 체크용 하트비트, 쿠키에 세션토큰 저장, 이벤트 등을 지원합니다.

UserApp을 사용해 보고 싶다면 Codecademy에 대한 과정을 수강하십시오.

다음은 동작의 예를 제시하겠습니다.

  • 오류 처리가 포함된 로그인 양식:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
    
  • 오류 처리가 포함된 등록 양식:

    <form ua-signup ua-error="error-msg">
      <input name="first_name" placeholder="Your name"><br>
      <input name="login" ua-is-email placeholder="Email"><br>
      <input name="password" placeholder="Password" type="password"><br>
      <button type="submit">Create account</button>
      <p id="error-msg"></p>
    </form>
    
  • 퍼블릭 루트 및 로그인 폼인 루트를 지정하는 방법:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    

    .otherwise()는 loginroute 후 .§:

    $routeProvider.otherwise({redirectTo: '/home'});

  • 로그아웃 링크:

    <a href="#" ua-logout>Log Out</a>

    (세션을 종료하고 로그인 루트로 리다이렉트)

  • 사용자 속성 액세스:

    에 액세스하려면 , 「」를 합니다.user: 「」):user.current.email

    템플릿: " " " " 입니다.<span>{{ user.email }}</span>

  • 로그인 시에만 표시되어야 하는 요소 숨기기:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

  • 권한에 따라 요소 표시:

    <div ua-has-permission="admin">You are an admin</div>

서비스에 대한 하려면 " "를 하십시오.user.token()세션 토큰을 가져와 AJAX 요구와 함께 전송합니다.백엔드에서 UserApp API(UserApp을 사용하는 경우)를 사용하여 토큰이 유효한지 확인합니다.

도움이 필요하시면 연락주세요:)

이 기사를 요약한 github repo를 작성했습니다.https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login Github repo

플런커

가능한 한 잘 설명하겠습니다.여러분들을 도와드리겠습니다.

(1) app.js : 앱 정의 시 인증 상수 생성

var loginApp = angular.module('loginApp', ['ui.router', 'ui.bootstrap'])
/*Constants regarding user login defined here*/
.constant('USER_ROLES', {
    all : '*',
    admin : 'admin',
    editor : 'editor',
    guest : 'guest'
}).constant('AUTH_EVENTS', {
    loginSuccess : 'auth-login-success',
    loginFailed : 'auth-login-failed',
    logoutSuccess : 'auth-logout-success',
    sessionTimeout : 'auth-session-timeout',
    notAuthenticated : 'auth-not-authenticated',
    notAuthorized : 'auth-not-authorized'
})

(2) 인증 서비스:다음 기능은 모두 auth.js 서비스에 구현되어 있습니다.$http 서비스는 인증 절차를 위해 서버와 통신하기 위해 사용됩니다.또한 인증에 관한 기능, 즉 사용자가 특정 액션을 수행할 수 있는지 여부도 포함됩니다.

angular.module('loginApp')
.factory('Auth', [ '$http', '$rootScope', '$window', 'Session', 'AUTH_EVENTS', 
function($http, $rootScope, $window, Session, AUTH_EVENTS) {

authService.login() = [...]
authService.isAuthenticated() = [...]
authService.isAuthorized() = [...]
authService.logout() = [...]

return authService;
} ]);

(3) 세션:사용자 데이터를 보관하는 싱글톤.여기서의 실장은, 고객에게 달려 있습니다.

angular.module('loginApp').service('Session', function($rootScope, USER_ROLES) {

    this.create = function(user) {
        this.user = user;
        this.userRole = user.userRole;
    };
    this.destroy = function() {
        this.user = null;
        this.userRole = null;
    };
    return this;
});

(4) 부모 컨트롤러:이것을 애플리케이션의 「메인」기능이라고 하면, 모든 컨트롤러가 이 컨트롤러로부터 계승되어 이 앱의 인증의 backbone이 됩니다.

<body ng-controller="ParentController">
[...]
</body>

(5) 접근통제:특정 루트에 대한 접근을 거부하려면 다음 2단계를 구현해야 합니다.

a) 아래와 같이 UI 라우터의 $stateProvider 서비스에 각 루트에 액세스할 수 있는 역할의 데이터를 추가합니다(ngRoute에서도 동일하게 동작할 수 있습니다.

.config(function ($stateProvider, USER_ROLES) {
  $stateProvider.state('dashboard', {
    url: '/dashboard',
    templateUrl: 'dashboard/index.html',
    data: {
      authorizedRoles: [USER_ROLES.admin, USER_ROLES.editor]
    }
  });
})

b) $rootScope로.$on440$상태Change Start') 사용자가 인증되지 않은 경우 상태 변경을 방지하기 위해 기능을 추가합니다.

$rootScope.$on('$stateChangeStart', function (event, next) {
    var authorizedRoles = next.data.authorizedRoles;
    if (!Auth.isAuthorized(authorizedRoles)) {
      event.preventDefault();
      if (Auth.isAuthenticated()) {
        // user is not allowed
        $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
      } else {d
        // user is not logged in
        $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
      }
    }
});

(6) 인증 가로채기:이것은 실장되어 있습니다만, 이 코드의 범위에서는 확인할 수 없습니다.각 $http 요구 후 이 대행 수신기는 상태 코드를 체크하고 다음 중 하나가 반환되면 이벤트를 브로드캐스트하여 사용자가 다시 로그인하도록 강제합니다.

angular.module('loginApp')
.factory('AuthInterceptor', [ '$rootScope', '$q', 'Session', 'AUTH_EVENTS',
function($rootScope, $q, Session, AUTH_EVENTS) {
    return {
        responseError : function(response) {
            $rootScope.$broadcast({
                401 : AUTH_EVENTS.notAuthenticated,
                403 : AUTH_EVENTS.notAuthorized,
                419 : AUTH_EVENTS.sessionTimeout,
                440 : AUTH_EVENTS.sessionTimeout
            }[response.status], response);
            return $q.reject(response);
        }
    };
} ]);

추신: 첫 번째 기사에서 설명한 바와 같이 data autofill 형식의 버그는 directives.js에 포함된 디렉티브를 추가하면 쉽게 피할 수 있습니다.

추신.2 이 코드는 사용자가 쉽게 조정할 수 있으며, 다른 경로를 볼 수 있도록 하거나, 표시해야 할 내용이 아닌 내용을 표시할 수 있습니다.논리는 서버 측에서 구현해야 합니다. 이는 단지 ng-app에서 올바르게 표시하는 방법일 뿐입니다.

언급URL : https://stackoverflow.com/questions/18325324/angularjs-authentication-restful-api

반응형