MEAN堆栈如何处理登录和重定向

时间:2016-10-16 02:24:20

标签: angularjs node.js authentication angular-ui-router passport.js

我有一个简单的MEAN堆栈应用程序,只是尝试了解身份验证并使其工作。

您可以在github中查看完整的程序:https://github.com/7seven7lst/chatterApp

我在Angular(app.js)中有以下内容,其中配置文件路由受到限制:

angular.module('chat', ['ui.router', 'chat.main', 'chat.signin', 'chat.profile'])
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {

    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('home', {
            url: '/',
            templateUrl: 'views/main.html',
            controller: 'MainCtrl'
        })
        .state('signin', {
            url: '/signin',
            templateUrl: 'views/signin.html',
            controller: 'SigninCtrl'
        })
        .state('profile', {
            url: '/profile', 
            templateUrl: 'views/profile.html',
            controller: 'ProfileCtrl'
        });

    // the following will give angular injection error
    /*
    $httpProvider.interceptors.push(function($q, $state) {
      return {
        response: function(response) {
          // do something on success
          return response;
        },
        responseError: function(response) {
          if (response.status === 401) {
            $state.go('home');
          } 
          return $q.reject(response);
        }
      };
    });
    */

});

我有NodeJS功能来处理身份验证:

var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var path = require('path');
var session = require('express-session');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

// setup passport
passport.use(new LocalStrategy(
  function(username, password, done){
    if (username !== 'test' || password !='password'){
      console.log("not loged in ");
      return done(null, false); // login failed
    } else {
      console.log("somehow we are here");
      return done(null, {username: username});
    }
  }

));



passport.serializeUser(function(user, done){
  done(null, user);
});
passport.deserializeUser(function(user, done){
  done(null, user);
})

// middleware function to handle protected route
var requireAuthentication = function(req, res, next){
  if (req.isAuthenticated()){
    next();
  } else {
    console.log("401 should be sent here...");
    res.end(401);
    //res.redirect('/login');
  }
}

var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false })); // middleware to parse the form data
app.use(express.static(__dirname + '/public')); // server static file in public folder. 
app.use('/bower_components',  express.static(__dirname + '/bower_components')); 
app.use(cookieParser()); 
app.use(session({
  secrete: 'make this a good secret',
  resave: false,
  saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());


app.get('/', function(req, res){
  console.log("here, homepage");
  res.sendFile('index.html');
})


app.post('/signin', passport.authenticate('local', {
  successRedirect: '/profile', failureRedirect: '/login'
}), function(req, res){
  res.send(req.user);
});


app.get('/profile', [requireAuthentication,
function(req, res){
  console.log("here, profile page");
  res.json(200, {username: req.session.passport.user.username});
}])

app.get('*', function(req, res){
  res.redirect('/');
})

app.listen(8080, function(){
  console.log("listening on port 8080");
})

我测试了我的应用程序,并且没有从服务器发送过401。如果我试图取消注释上面的线条,角度拦截器似乎不起作用。 如何让受保护的路线在anuglar工作?

1 个答案:

答案 0 :(得分:2)

从查看存储​​库代码看,signin.html中的表单似乎试图在提交时调用signin函数。此功能未定义,因此没有请求发送到服务器,因此没有响应。

编辑:

处理MEAN应用程序的身份验证时,有两个单独的注意事项。在服务器和客户端上完成身份验证。

服务器

在服务器上,对于已使用requireAuthentication中间件的路由,如果用户未经过身份验证,客户端将收到401状态代码响应。

如果您输入localhost:8080/profile,则在浏览器中,您将收到服务器的401响应。

<强>客户端

在客户端上,因为您未指定要使用html5mode。客户端路由使用片段url(以#开头的url部分)完成。 url的那部分不会发送到服务器。如果您向localhost:8080/#/profile发出请求,则服务器在http请求中获取的路径为/

在服务器上配置了/路由,以便在您的情况下使用index.html文件进行响应。然后index.html发出加载角度脚本并运行客户端代码的请求。

此时ui.router将处理客户端路由。这是您需要在客户端上进行身份验证检查的地方。有几种策略用于指示状态需要认证。

一种方法是在实际的状态定义上加上标记:

$stateProvider
  .state('home', {
    url: '/',         // is actually /#/
    templateUrl: 'views/main.html',
    controller: 'MainCtrl'
  })
...
  .state('profile', {
    url: '/profile',  // is actually /#/profile
    templateUrl: 'views/profile.html',
    controller: 'ProfileCtrl',
    authRequired: true
  });

然后添加一个运行块,为stateChangeStart事件添加一个listencope:

app.run(function($rootScope, $state, Auth) {
  $rootScope.$on('$stateChangeStart', function(event, nextState, params) {
    if (nextState.authRequired && !Auth.isAuthenticated()) {
      $state.go('signin');
    }
  });
});

auth服务需要知道用户是否经过身份验证,可能需要第一次进行服务器往返。 stackoverflow上有很多关于如何做到这一点的答案。

另一部分是$httpProvider.interceptors部分。从您的角度客户端,如果您向服务器请求需要身份验证的路由,请在示例中/profile(不是/#/profile,请记住这只是/),并且您是未经过身份验证,您将收到401响应。 auth拦截器部分告诉角度客户端如何在这种情况下做出响应。

.config(function($stateProvider, $urlRouterProvider, $httpProvider, $injector) {

...

  $httpProvider.interceptors.push(function($q) {
    return {
      responseError: function(response) {
        if (response.status === 401) {
          $injector.get('$state').go('signin');
        }
        return $q.reject(response);
      }
    };
  });
});