最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Server side routes in Iron Router and Meteor - Stack Overflow

programmeradmin1浏览0评论

Forward

It seems that in Meteor, we cannot call a server side route to render a file to the page without some sort of work-around from our normal workflow, from what I've read about server side routes. I hope I'm wrong about this, and there's a simple way to achieve what I'm looking to do...

** Sorry if this is a little long, but I think in this case providing more background and context is warranted **

Software/Versions

I'm using the latest Iron Router 1.* and Meteor 1.* and to begin, I'm just using accounts-password.

Background/Context

I have an onBeforeAction that simply redirects the user to either the wele page or home page base upon if the user is logged in or not:

both/routes.js

Router.onBeforeAction(function () {
  if (!Meteor.user() || Meteor.loggingIn())
    this.redirect('wele.view');
  else
    this.next();
  }
  ,{except: 'wele.view'}
);

Router.onBeforeAction(function () {
  if (Meteor.user())
    this.redirect('home.view');
  else
    this.next();
  }
  ,{only: 'wele.view'}
);

In the same file, both/routes.js, I have a simple server side route that renders a pdf to the screen, and if I remove the onBeforeAction code, the route works (the pdf renders to the page):

Router.route('/pdf-server', function() {
  var filePath = process.env.PWD + "/server/.files/users/test.pdf";
  console.log(filePath);
  var fs = Npm.require('fs');
  var data = fs.readFileSync(filePath);
  this.response.write(data);
  this.response.end();
}, {where: 'server'});

Exception thrown on Server Route

It's beside the point, but I get an exception when I add the above server side route to the file and take the route /pdf-server, while keeping the onBeforeAction code in place.

Insights into the exception can be found here: SO Question on Exception

Solution to the Exception

The main gist of the answer in the SO Question above is "You use Meteor.user() in your Route.onBeforeAction but it has no access to this information", and when, "your browser make a GET/POST request" [Server side route?], "to the server it doesn't have any information regarding the user's authentication state."

The solution, according to the same SO answerer is to "find an alternative way to authenticate the user," and one way to do this is to use "cookies'".

So, following up on this, I found another SO answer (by the same answerer as before), where a method to set and get the cookies is outlined here: SO Cookies technique

** So, to summarize, in order to allow for server side routes, it is suggested I use cookies instead of something like Meteor.userId() or this.userId. **

Cookie related code added

So I added the following code to my project: client/main.js

Deps.autorun(function() {
    if(Accounts.loginServicesConfigured() && Meteor.userId()) {
        setCookie("meteor_userid",Meteor.userId(),30);
        setCookie("meteor_logintoken",localStorage.getItem("Meteor.loginToken"),30);
    }
}); 

In my server side route, I changed the route to this:

both/routes.js

Router.route('/pdf-server', function() {
  //Parse cookies using get_cookies function from : 
  var userId = get_cookies(req)['meteor_usserid'];
  var loginToken = get_cookies(req)['meteor_logintoken'];
  var user = Meteor.users.findOne({_id:userId, "services.resume.loginTokens.token":loginToken});
  var loggedInUser = (user)?user.username : "Not logged in";

  var filePath = process.env.PWD + "/server/.files/users/test.pdf";
  console.log(filePath);
  var fs = Npm.require('fs');
  var data = fs.readFileSync(filePath);
  this.response.write(data);
  this.response.end();
}, {where: 'server'});

But this does not work as expected, the setCookie code is not valid for some reason.

My Questions

Question 1: Setting/getting the cookies in the manner depicted in the SO Cookies technique doesn't seem to work for me, does this technique still work in '15?

Question 2: Using cookies, how do I inform the server of the authentication state based on these cookies? Or, another way, How does adding the cookie check in my server side route "inform" the server about the user? I could check for anything in this route really; I could reject any user, but somehow the server needs to "know" about the user logged in right?

Question 3: Is cookies the best way to go about this, or is there a simpler way to achieve the same thing?

Side Question: I've seen a few places where middle ware is used for server side routes, for example:

WebApp.connectHandlers.stack.splice(...);

WebApp.connectHandlers.use(function(...) ...);

But none of these examples had security inside, will using middle ware in this way allow me to get around my problem?

Forward

It seems that in Meteor, we cannot call a server side route to render a file to the page without some sort of work-around from our normal workflow, from what I've read about server side routes. I hope I'm wrong about this, and there's a simple way to achieve what I'm looking to do...

** Sorry if this is a little long, but I think in this case providing more background and context is warranted **

Software/Versions

I'm using the latest Iron Router 1.* and Meteor 1.* and to begin, I'm just using accounts-password.

Background/Context

I have an onBeforeAction that simply redirects the user to either the wele page or home page base upon if the user is logged in or not:

both/routes.js

Router.onBeforeAction(function () {
  if (!Meteor.user() || Meteor.loggingIn())
    this.redirect('wele.view');
  else
    this.next();
  }
  ,{except: 'wele.view'}
);

Router.onBeforeAction(function () {
  if (Meteor.user())
    this.redirect('home.view');
  else
    this.next();
  }
  ,{only: 'wele.view'}
);

In the same file, both/routes.js, I have a simple server side route that renders a pdf to the screen, and if I remove the onBeforeAction code, the route works (the pdf renders to the page):

Router.route('/pdf-server', function() {
  var filePath = process.env.PWD + "/server/.files/users/test.pdf";
  console.log(filePath);
  var fs = Npm.require('fs');
  var data = fs.readFileSync(filePath);
  this.response.write(data);
  this.response.end();
}, {where: 'server'});

Exception thrown on Server Route

It's beside the point, but I get an exception when I add the above server side route to the file and take the route /pdf-server, while keeping the onBeforeAction code in place.

Insights into the exception can be found here: SO Question on Exception

Solution to the Exception

The main gist of the answer in the SO Question above is "You use Meteor.user() in your Route.onBeforeAction but it has no access to this information", and when, "your browser make a GET/POST request" [Server side route?], "to the server it doesn't have any information regarding the user's authentication state."

The solution, according to the same SO answerer is to "find an alternative way to authenticate the user," and one way to do this is to use "cookies'".

So, following up on this, I found another SO answer (by the same answerer as before), where a method to set and get the cookies is outlined here: SO Cookies technique

** So, to summarize, in order to allow for server side routes, it is suggested I use cookies instead of something like Meteor.userId() or this.userId. **

Cookie related code added

So I added the following code to my project: client/main.js

Deps.autorun(function() {
    if(Accounts.loginServicesConfigured() && Meteor.userId()) {
        setCookie("meteor_userid",Meteor.userId(),30);
        setCookie("meteor_logintoken",localStorage.getItem("Meteor.loginToken"),30);
    }
}); 

In my server side route, I changed the route to this:

both/routes.js

Router.route('/pdf-server', function() {
  //Parse cookies using get_cookies function from : https://stackoverflow./questions/3393854/get-and-set-a-single-cookie-with-node-js-http-server
  var userId = get_cookies(req)['meteor_usserid'];
  var loginToken = get_cookies(req)['meteor_logintoken'];
  var user = Meteor.users.findOne({_id:userId, "services.resume.loginTokens.token":loginToken});
  var loggedInUser = (user)?user.username : "Not logged in";

  var filePath = process.env.PWD + "/server/.files/users/test.pdf";
  console.log(filePath);
  var fs = Npm.require('fs');
  var data = fs.readFileSync(filePath);
  this.response.write(data);
  this.response.end();
}, {where: 'server'});

But this does not work as expected, the setCookie code is not valid for some reason.

My Questions

Question 1: Setting/getting the cookies in the manner depicted in the SO Cookies technique doesn't seem to work for me, does this technique still work in '15?

Question 2: Using cookies, how do I inform the server of the authentication state based on these cookies? Or, another way, How does adding the cookie check in my server side route "inform" the server about the user? I could check for anything in this route really; I could reject any user, but somehow the server needs to "know" about the user logged in right?

Question 3: Is cookies the best way to go about this, or is there a simpler way to achieve the same thing?

Side Question: I've seen a few places where middle ware is used for server side routes, for example:

WebApp.connectHandlers.stack.splice(...);

WebApp.connectHandlers.use(function(...) ...);

But none of these examples had security inside, will using middle ware in this way allow me to get around my problem?

Share Improve this question edited May 23, 2017 at 12:33 CommunityBot 11 silver badge asked Dec 31, 2014 at 0:32 AaronAaron 3,0682 gold badges22 silver badges46 bronze badges 5
  • Meteor doesn't include cookie support by default because cookies enable a large number of attack vectors, e.g. CSRF. Unfortunately, server-side routes don't have access to authentication information so you are left with devising a system which you feel sufficiently proves that the user is who they say they are. This usually takes the form of some kind of token or key. That key could be stored in a cookie, but it doesn't have to be. – David Weldon Commented Jan 1, 2015 at 18:16
  • In your particular use case, does the pdf-server route need to know which user is making the request, or is any authenticated user sufficient? – David Weldon Commented Jan 1, 2015 at 18:20
  • 1 David, no, that's one of the parts that I really don't fully understand. I haven't told the server it needs to authenticate the user for that route. Somehow, onBeforeAction is the code throwing the error when I take the pdf-server route. So I suppose, when the Server side route is taken, onBeforeAction is called first, and tries to check for Meteor.user(), and since it's a Server side route, there is no Meteor.user(), so it throws. On the client side, with onBeforeAction, the routes are fine because Meteor.user() exists there. – Aaron Commented Jan 1, 2015 at 18:30
  • 1 So, I'm guessing I should set the cookies (the cookie code above doesn't work right now) and check for that in onBeforeAction instead of Meteor.user()... – Aaron Commented Jan 1, 2015 at 18:36
  • Oh, I see the source of the confusion now. I'll add an answer but we may need to iterate on it together to get something working. – David Weldon Commented Jan 1, 2015 at 18:50
Add a ment  | 

1 Answer 1

Reset to default 8

Your server-side routes are running the global onBeforeAction code (it's defined in a shared directory), which breaks because the server routes are simple REST endpoints which don't understand user authentication information (i.e. Meteor.user() doesn't work). The solution is to wrap the client-specific onBeforeAction calls with Meteor.isClient or simply move that code under the client directory. For example:

if (Meteor.isClient) {
  Router.onBeforeAction(function () {
    if (!Meteor.user() || Meteor.loggingIn())
      this.redirect('wele.view');
    else
      this.next();
    }
    ,{except: 'wele.view'}
  );

  Router.onBeforeAction(function () {
    if (Meteor.user())
      this.redirect('home.view');
    else
      this.next();
    }
    ,{only: 'wele.view'}
  );
}

Router.route('/pdf-server', function() {
  ...
}, {where: 'server'});
发布评论

评论列表(0)

  1. 暂无评论