1

Angular 및 Breeze를 사용하는 SPA 앱이 있으며 로그인 기능을 구현해야하며 Angular/Breeze를 처음 사용합니다. 아래에 언급 한 바와 같이 내 건축/코드 구조는 다음과 같습니다Angular/Breeze 로그인 구현

login.html -> login.js -> 데이터 컨텍스트/Service.js --->의 EntityManagerFactory -> breezecontroller.cs -> repository-> dbcontext -> 데이터베이스.

나는 다음과 같은 도전에 직면하고있다 :

  1. 내가 기본값으로 로그인 페이지를 표시 할 수 없습니다 오전, 난 항상 기본 페이지로 대시 보드를 얻고있다. 로그인 페이지로 연결할 수있는 위치를 찾고 있습니다. 2.breezecontroller - 컨트롤러 내부에 로그인 방법을 작성해야합니까?

전반적으로 아키텍처/코드 구조를 따르는 완전한 로그인 기능 구현을 찾고 있습니다.

+0

큰 질문입니다. 다행이 대답했습니다. 나는 그것을 추가 할 것이다 ** 나는 브리즈 컨트롤러에 로그인을하지 않을 것이다 **. 인증 및 권한 부여는 별도의 관심사입니다. – Ward

답변

2

다음은 각도 기반 SPA에서 사용할 수있는 방법에 대한 설명입니다. 이 특정 예제는 토큰 기반 OAuth 인증을 사용하지만 다른 인증 체계에 적용될 수 있습니다. 그것은 느슨하게 Authentication in AngularJS (or similar) based application

일부 하이라이트에 기술 된 방법에 근거는 다음과 같습니다

  • 인증은 auth 서비스를 통해 관리됩니다.

  • HTTP 요청을 차단하고 있습니다 :

    • 401 (액세스 거부) 오류가 감지되고 어떤 사용자가 auth:login 이벤트가 방출되고, 로그인되지 않은 경우 - $rootScope

      에 (주하지 방송)
    • 사용자가 로그인되어 있고 OAuth 새로 고침 토큰을 사용할 수있는 동안 401 오류가 감지되면 새로 고침 토큰을 기반으로 새 액세스 토큰을 얻으려고합니다. auth:login 이벤트는 토큰을 새로 고칠 수없는 경우에만 생성됩니다.

    • 일단 사용자가 로그인하면 서버가 사용자를 인증 할 수 있도록 사용자의 액세스 토큰을 포함하는 Authorization 헤더가 각 HTTP 요청에 삽입됩니다.

  • 응용 프로그램은 auth:login 이벤트를보고 사용자에게 자격 증명을하라는 메시지를 표시합니다. (이 작업을 위해 Angular-UI Bootstrap modal dialog을 사용합니다.) 자격 증명이 제공되면 서비스의 login 함수를 호출하여 로그인을 완료해야합니다. login이 호출 된 후 초기에 401 오류로 실패한 보류중인 모든 HTTP 요청이 다시 시도됩니다. 또는 auth 서비스의 loginCancelled 함수를 호출하여 로그인을 취소하면 보류중인 모든 HTTP 요청이 거부됩니다.

    auth.login(userName, password, isPersistent) 
        .success(function() { 
         // Dismiss login dialog here 
        }) 
        .error(function (data, status) { 
         if (status === 401 || (data && data.error === 'invalid_grant')) { 
          failureMessage = 'Log in failed: Bad username or password'; 
         } else { 
          failureMessage = 'Log in failed: Unexpected error'; 
         } 
        }); 
    
    • 로그인 한 사용자 window.sessionStorage에 저장된 세부 예 :

      : 사용자가 인증 정보를 제공 한 후 여기

    angular.module('app', ['auth']) 
    .run(['$rootScope', 'auth', function ($rootScope, auth) { 
        $rootScope.$on(auth.options.loginRequiredEvent, function (event, details) { 
         // Display login dialog here, which will ultimately 
         // call `auth.login` or `auth.loginCancelled` 
        }); 
    
        auth.restoreAuthDataFromStorage(); 
    }]); 
    

    auth.login 호출의 예 또는 window.localStorage (영구 로그인이 요청되었는지 여부에 따라) 페이지로드를 통해 액세스 할 수 있습니다.

마지막으로 여기에 auth 서비스 자체가 있습니다.

var module = angular.module('auth'); 

module.provider('auth', function() { 
    var authOptions = { 
     tokenUrl: '/OAuthToken', 
     loginRequiredEvent: 'auth:loginRequired', 
     logoffEvent: 'auth:logoff', 
     loginEvent: 'auth:login', 
     authTokenKey: 'auth:accessToken' 
    }; 

    this.config = function (options) { 
     angular.extend(authOptions, options); 
    }; 

    // Get the auth service 
    this.$get = ['$rootScope', '$http', '$q', function ($rootScope, $http, $q) { 
     var authData = { 
      // Filled as follows when authenticated: 
      // currentUserName: '...', 
      // accessToken: '...', 
      // refreshToken: '...', 
     }; 

     var httpRequestsPendingAuth = new HttpRequestsPendingAuthQueue(); 

     // Public service API 
     return { 
      login: login, 
      refreshAccessToken: refreshAccessToken, 
      loginCancelled: loginCancelled, 
      logoff: logoff, 
      currentUserName: function() { return authData.currentUserName; }, 
      isAuthenticated: function() { return !!authData.accessToken; }, 
      getAccessToken: function() { return authData.accessToken; }, 
      restoreAuthDataFromStorage: restoreAuthDataFromStorage, 
      _httpRequestsPendingAuth: httpRequestsPendingAuth, 
      options: authOptions, 
     }; 

     function isAuthenticated() { 
      return !!authData.accessToken; 
     }; 

     function restoreAuthDataFromStorage() { 
      // Would be better to use an Angular service to access local storage 
      var dataJson = window.sessionStorage.getItem(authOptions.authTokenKey) || window.localStorage.getItem(authOptions.authTokenKey); 

      authData = (dataJson ? JSON.parse(dataJson) : {}); 
     } 

     function accessTokenObtained(data) { 
      if (!data || !data.access_token) { 
       throw new Error('No token data returned'); 
      } 

      angular.extend(authData, { 
       accessToken: data.access_token, 
       refreshToken: data.refresh_token 
      }); 

      // Would be better to use an Angular service to access local storage 
      var storage = (authData.isPersistent ? window.localStorage : window.sessionStorage); 
      storage.setItem(authOptions.authTokenKey, JSON.stringify(authData)); 

      httpRequestsPendingAuth.retryAll($http); 
     } 

     function login(userName, password, isPersistent) { 
      // Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded 
      var data = 'grant_type=password&username=' + encodeURIComponent(userName) + '&password=' + encodeURIComponent(password); 

      return $http 
       .post(authOptions.tokenUrl, data, { ignoreAuthFailure: true }) 
       .success(function (data) { 
        authData = { 
         currentUserName: userName, 
         isPersistent: isPersistent 
        }; 

        accessTokenObtained(data); 

        $rootScope.$emit(authOptions.loginEvent); 
       }) 
       .error(function() { 
        logoff(); 
       }); 
     } 

     function refreshAccessToken() { 
      if (!authData.refreshToken) { 
       logoff(); 
       return $q.reject('No refresh token available'); 
      } 

      // Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded 
      var data = 'grant_type=refresh_token&refresh_token=' + encodeURIComponent(authData.refreshToken); 

      return $http 
       .post(authOptions.tokenUrl, data, { ignoreAuthFailure: true }) 
       .success(function (data) { accessTokenObtained(data); }) 
       .error(function() { logoff(); }); 
     } 

     function loginCancelled() { 
      httpRequestsPendingAuth.rejectAll(); 
     } 

     function logoff() { 
      // Would be better to use an Angular service to access local storage 
      window.sessionStorage.removeItem(authOptions.authTokenKey); 
      window.localStorage.removeItem(authOptions.authTokenKey); 

      if (isAuthenticated()) { 
       authData = {}; 

       $rootScope.$emit(authOptions.logoffEvent); 
      } 
     } 

     // Class implementing a queue of HTTP requests pending authorization 
     function HttpRequestsPendingAuthQueue() { 
      var q = []; 

      this.append = function (rejection, deferred) { 
       q.push({ rejection: rejection, deferred: deferred }); 
      }; 

      this.rejectAll = function() { 
       while (q.length > 0) { 
        var r = q.shift(); 
        r.deferred.reject(r.rejection); 
       } 
      }; 

      this.retryAll = function ($http) { 
       while (q.length > 0) { 
        var r = q.shift(); 
        retryRequest($http, r.rejection.config, r.deferred); 
       } 
      }; 

      function retryRequest($http, config, deferred) { 
       var configToUse = angular.extend(config, { ignoreAuthFailure: true }); 

       $http(configToUse) 
        .then(function (response) { 
         deferred.resolve(response); 
        }, function (response) { 
         deferred.reject(response); 
        }); 
      } 
     } 
    }]; 
}); 

module.config(['$httpProvider', function ($httpProvider) { 
    $httpProvider.interceptors.push(['$injector', '$rootScope', '$q', function ($injector, $rootScope, $q) { 
     var auth; 

     return { 
      // Insert an "Authorization: Bearer <token>" header on each HTTP request 
      request: function (config) { 
       auth = auth || $injector.get('auth'); 

       var token = auth.getAccessToken(); 
       if (token) { 
        config.headers = config.headers || {}; 
        config.headers.Authorization = 'Bearer ' + token; 
       } 

       return config; 
      }, 

      // Raise a "login required" event upon "401 access denied" responses on HTTP requests 
      responseError: function(rejection) { 
       if (rejection.status === 401 && !rejection.config.ignoreAuthFailure) { 
        var deferred = $q.defer(); 

        auth = auth || $injector.get('auth'); 

        auth._httpRequestsPendingAuth.append(rejection, deferred); 

        if (auth.isAuthenticated()) { 
         auth.refreshAccessToken().then(null, function() { 
          $rootScope.$emit(auth.options.loginRequiredEvent, { message: 'Login session has timed out. Please log in again.' }); 
         }); 
        } else { 
         // Not currently logged in and a request for a protected resource has been made: ask for a login 
         $rootScope.$emit(auth.options.loginRequiredEvent, { rejection: rejection }); 
        } 

        return deferred.promise; 
       } 

       // otherwise, default behaviour 
       return $q.reject(rejection); 
      } 
     }; 
    }]); 
}]);