【问题标题】:AngularJS and ExpressJS session management?AngularJS 和 ExpressJS 会话管理?
【发布时间】:2013-08-31 20:57:35
【问题描述】:

我想在所有页面上保持会话。对于这个项目,我使用 expressJs、nodeJS 作为服务器端。 AngularJS 在前端。

我不确定,当视图更改或 url 更改时如何处理会话。因为我需要同时处理 expressJS 路由器或 angularJs 路由器

我应该遵循什么方法?

angularJS 路由器

     myApp.config(['$routeProvider', function($routeProvider) {

    $routeProvider.when('/welcome', {templateUrl: 'partials/welcome.html', controller: 'MyCtrl2'});
    $routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'MyCtrl2'});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', controller: 'singupController'});
    $routeProvider.otherwise({redirectTo: '/'});
  }]);

注册控制器

myApp.controller('singupController',function($scope,$rootScope,$http){

    $scope.doSingnup = function() {

       var formData = {
          'username' : this.username,
          'password' : this.password,
           'email' : null
      };

      var jdata = JSON.stringify(formData);

      $http({method:'POST',url:'/signup',data:jdata})
      .success(function(data,status,headers,config){

                console.log(data);

      }).
      error(function(data,status,headers,config){

        console.log(data)

      });
    }
  })

ExpressJS 路由器

    module.exports = exports = function(app, db) {

    var sessionHandler = new SessionHandler(db);
    var contentHandler = new ContentHandler(db);

    // Middleware to see if a user is logged in
    app.use(sessionHandler.isLoggedInMiddleware);

    app.get('/', contentHandler.displayMainPage);

    app.post('/login', sessionHandler.handleLoginRequest);

    app.get('/logout', sessionHandler.displayLogoutPage);

    app.get("/welcome", sessionHandler.displayWelcomePage);

    app.post('/signup', sessionHandler.handleSignup);

    app.get('*', contentHandler.displayMainPage);

    // Error handling middleware
    app.use(ErrorHandler);
}

注册后,我想重定向到登录页面。我怎样才能在上面的路由器中做到这一点。我应该使用以下哪一项来更改应用程序的视图
1) angularJS的$位置
2) ExpressJS 的重定向

【问题讨论】:

    标签: node.js angularjs express


    【解决方案1】:

    所以我遇到了同样的问题,公平地说,我可能在某个我不记得的地方读到了这种方法。

    问题:Angular 构建单页应用程序。刷新后,您将失去作用域以及经过身份验证的用户。

    接近

    AngularJS modules 提供了一个名为 run 的启动函数,该函数总是在页面加载时调用。非常适合刷新/重新加载。

    myApp.run(function ($rootScope, $location, myFactory) {
        $http.get('/confirm-login')
            .success(function (user) {
                if (user && user.userId) {
                    $rootScope.user = user;
                }
            });
    }
    

    express-session 为您保存会话并使用浏览器发送的 sessionId 对您进行身份验证。所以它总是知道你是否通过了身份验证。

    router.get('/confirm-login', function (req, res) {
            res.send(req.user)
        }
    );
    

    我所要做的就是,在刷新并加载所有依赖项后,询问我是否已通过身份验证并设置 $rootScope.user = authenticatedUserFromExpress;

    【讨论】:

    • // , 你知道有谁用过这个吗?非常感谢 Github 链接。
    • 对于那些浏览此答案的人,请确保为您的系统添加额外的安全性,因为这会导致潜在的漏洞
    • @N15M0_jk 如果你能解决这个问题,你能帮我吗
    【解决方案2】:

    这里有两个不同的概念——Angular 中的服务器端会话状态和客户端的用户状态。在 express 中,您可以通过 req.session 使用会话来管理基于会话的数据。

    在角度方面,您的控制器中只有范围。如果你想跨多个控制器跟踪一些数据,你需要创建一个服务来存储数据并将服务注入你需要的控制器。

    一个典型的生命周期是首先检查服务中是否已经有数据,如果有就使用它。如果没有,请等待(由用户或应用程序或其他)填充数据,然后检测这些更改并与您的服务同步。

    【讨论】:

    • 我应该使用哪个来改变视图,expressJS res.redirect OR $location.path() of angularJS
    • 注册完成后,设置服务器端标志以指示用户已通过身份验证,然后将其发送到具有 Angular 应用程序的页面。如果您在 Angular 应用页面上强制执行(服务器端)身份​​验证,则只有登录的用户才能点击它。不要让注册的用户再次登录。
    • // ,感谢您的通用解决方案。你知道任何开源项目(可能发布到 Github),如果有的话,是这个“典型生命周期”的例子吗?
    • 如果这里能提供一些进一步的信息来为 OPs 问题绘制一个程序化的解决方案,那就太好了。
    【解决方案3】:

    注册控制器

    function SignupCtrl($scope, $http, $location) {
        $scope.form = {}; // to capture data in form 
        $scope.errorMessage = ''; // to display error msg if have any
      $scope.submitPost = function() { // this is to submit your form can't do on
                                        //traditional way because it against angularjs SPA
        $http.post('/signup', $scope.form).
          success(function(data) {    // if success then redirect to "/" status code 200
            $location.path('/');
          }).error(function(err) {   // if error display error message  status code 400
                                     // the form can't be submitted until get the status code 200
            $scope.errorMessage = err;
          });
      };
    }
    

    sessionHandler.handleSignup

     this.handleSignup = function(req, res, next) {
            "use strict";
              // if you have a validate function pass the data from your
              // Signup controller to the function in my case is validateSignup
              // req.body is what you need 
            validateSignup(req.body, function(error, data) { 
            if(error) {
                res.send(400, error.message); // if error send error message to angularjs
            }else {
                // do something else
                // rmb to res.send(200) 
    
            }
        });
    
    }
    

    验证注册

    function validateSignup(data,callback) {
            "use strict";   // the data is req.body 
               //so now you can access your data on your form 
               // e.g you have 2 fields name="password" and name="confirmPassword on your form"
              var pass = data.password,
                  comPass = data.confirmPassword;
               if(pass != comPass){
                   callback(new Error('Password must match'), null); 
                      // then show the error msg on the form by using 
                      //angular ng-if like <div ng-if="errorMessage">{{errorMessage}}</div>
               }else{
                   callback(null, data);
                }
    
    
    
      }
    

    希望对你有所帮助

    【讨论】:

      【解决方案4】:

      在这里的所有答案中,我最喜欢@alknows 的方法。但是,与建议您向服务器发送请求以获取当前用户数据的其他答案一样,我对它们有几个问题:

      • 您必须处理 AJAX ($http) 调用导致的竞争条件。
      • 在服务器已经呈现您的 index.html 之后,您正在向服务器发送不必要的请求

      我尝试了@alknow 的方法,在我能够解决由于我的 Angular 应用程序控制器和配置需要当前用户完成他们的工作而出现的许多竞争条件之后,它对我有用。在适当的时候,我会尽力避免竞争条件,所以我有点不愿意继续这种方法。所以我想到了一个更好的方法:将当前用户数据与你的 index.html 一起发送并存储在本地。

      我的方法:将 currentUser 嵌入 index.html 并在客户端本地存储

      在您服务器上的index.html 中,创建一个脚本标签来保存您想要传递给客户端的任何数据:

      ```

      <!--YOUR OTHER index.html stuff go above here-->
      <script id="server-side-rendered-client-data" type="text/javascript">
          var __ssr__CData = {
              currentUser: { id: '12345', username: 'coolguy', etc: 'etc.' }
          }
      </script>
      

      ```

      然后,正如 @alknows 建议的那样,在 app.js 或您启动 Angular 应用程序的任何地方,添加 app.run(..., () =&gt; {...})。在 app.run() 中,您将需要获取服务器端呈现的客户端数据对象,我将其命名为 __ssr_CData,以便稍后在我的其他 javascript 中跨全局命名空间遇到名称冲突的可能性较小:

      var myAngularApp = angular.module("mainApp", ['ngRoute']);
      
      myAngularApp.run(function ($rootScope) {
          const currentUserFromServer = __ssr__CData.currentUser
          const currentUserAccessTokenFromServer = __ssr__CData.accessToken
          const currentUser = 
          CurrentUser.set(currentUserAccessTokenFromServer, currentUserFromServer)
          $rootScope.currentUser = currentUser
      });
      

      如您所知,每当页面完全重新加载时,都会调用app.run()CurrentUser 是一个全局类,用于在单页环境中管理我的 Angular 应用程序的当前用户。因此,当我调用CurrentUser.set(...) 时,它会将当前用户数据存储在一个地方,我稍后可以通过调用CurrentUser.get() 在我的 Angular 应用程序中检索。因此,在您的任何 Angular 应用程序控制器中,您现在可以通过简单地执行以下操作来检索服务器提供的当前用户:

      myAngularApp.controller('loginController',function($scope, $rootScope, $http){
         //check if the user is already logged in:
         var currentUser = CurrentUser.get()
         if(currentUser) {
             alert("HEY! You're already logged in as " +currentUser.username)
            return $window.location.href = "/";
         }
      
         //there is no current user, so let user log in
         //...
      }
      

      在该示例中,我使用了上面解释过的CurrentUser.get() 从服务器获取先前存储的当前用户。我也可以通过访问$rootScope.currentUser 来检索该当前用户,因为我也将它存储在那里。这取决于你。

      myAngularApp.controller('signupController',function($scope, $rootScope, $http){
         //check if the user is already logged in:
         var currentUser = CurrentUser.get()
         if(currentUser) {
             alert("HEY! You're already logged in as " +currentUser.username)
            return $window.location.href = "/";
         }
      
         //there is no current user, so let user signup
         //... you run your signup code after getting form data
         $http({method:'POST',url:'/signup',data:jdata})
             .success(function(data,status,headers,config){
                 //signup succeeded!
                 //set the current user locally just like in app.js
                 CurrentUser.set(data.newUser)
                 //send user to profile
                 return $window.location.href = "/profile";
             })
             .error(function(data,status,headers,config){
                 //something went wrong
                 console.log(data)
            });
      
      }
      

      现在,在新用户注册后,您的服务器会通过 AJAX 调用返回新用户。我们通过调用CurrentUser.set(...) 将该新用户设置为当前用户并将用户发送到他们的个人资料。您现在可以像检查当前用户是否存在于登录和注册控制器中一样,在配置文件控制器中获取当前用户。

      我希望这对遇到此问题的人有所帮助。供您参考,我使用client-sessions module 来处理我的服务器上的会话。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-23
        • 1970-01-01
        • 2020-12-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 2017-08-14
        • 2013-01-10
        相关资源
        最近更新 更多