首頁>Program>source

通過服務器侧路由對使用者进行身份驗證的最佳方法(最安全,最簡單)是什麼?

軟體/版本

我正在使用最新的Iron Router 1. *和Meteor 1. *,首先,我只是在使用帐戶密碼。

參考代碼

我有一个簡單的服務器端路由,可將pdf呈現到螢幕上:

both / routes.js

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'});

举例来說,我想做些与建議的答案如此

在服務器上:

var Secrets = new Meteor.Collection("secrets"); 
Meteor.methods({
  getSecretKey: function () {
    if (!this.userId)
      // check if the user has privileges
      throw Meteor.Error(403);
    return Secrets.insert({_id: Random.id(), user: this.userId});
  },
});

然後在客戶端代碼中:

testController.events({
  'click button[name=get-pdf]': function () {
      Meteor.call("getSecretKey", function (error, response) {
        if (error) throw error;
        if (response) 
          Router.go('/pdf-server');
      });
  }
});

但是,即使我以某種方式使此方法正常工作,除非路由本身以某種方式檢查了Secrets集合的正確性,否則我仍然容易受到使用者的干扰,只需輸入" / pdf-server"之類的URL?

在Route中,我可以获取請求,並以某種方式获取標頭資訊?

Router.route('/pdf-server', function() {
  var req = this.request;
  var res = this.response;
}, {where: 'server'});

然後从客戶端通過HTTP標頭傳遞一个令牌,然後在路由中檢查该令牌是否来自Collection?

最新回復
  • 11天前
    1 #

    除了使用url令牌作為其他答案之外,您還可以 也使用Cookie:

    添加一些允许您設置cookie並在服務器端讀取它们的軟體包:

    meteor add mrt:cookies thepumpinglemma:cookies
    

    然後,您可能会得到一些与您的登錄狀態同步的cookie

    Client Side

    Tracker.autorun(function() {
         //Update the cookie whenever they log in or out
         Cookie.set("meteor_user_id", Meteor.userId());
         Cookie.set("meteor_token", localStorage.getItem("Meteor.loginToken"));
    });
    

    Server Side

    在服務器端,您只需檢查此cookie是否有效(使用铁路由器)

    Router.route('/somepath/:fileid', function() {
       //Check the values in the cookies
       var cookies = new Cookies( this.request ),
           userId = cookies.get("meteor_user_id") || "",
           token = cookies.get("meteor_token") || "";
       //Check a valid user with this token exists
       var user = Meteor.users.findOne({
           _id: userId,
           'services.resume.loginTokens.hashedToken' : Accounts._hashLoginToken(token)
       });
       //If they're not logged in tell them
       if(!user) return this.response.end("Not allowed");
       //Theyre logged in!
       this.response.end("You're logged in!");
    }, {where:'server'});
    

  • 11天前
    2 #

    我认為我在IronRouter.route()中有一个安全,簡單的解決方案.必须在標頭中使用有效的使用者ID和auth令牌进行請求.我从Router.route()中呼叫此函式,然後使我可以訪問this.user,或者如果身份驗證失败,則返迴401:

    //  Verify the request is being made by an actively logged in user
    //  @context: IronRouter.Router.route()
    authenticate = ->
      // Get the auth info from header
      userId = this.request.headers['x-user-id']
      loginToken = this.request.headers['x-auth-token']
    // Get the user from the database
    if userId and loginToken
      user = Meteor.users.findOne {'_id': userId, 'services.resume.loginTokens.token': loginToken}
    // Return an error if the login token does not match any belonging to the user
    if not user
      respond.call this, {success: false, message: "You must be logged in to do this."}, 401
    // Attach the user to the context so they can be accessed at this.user within route
    this.user = user
    
    //  Respond to an HTTP request
    //  @context: IronRouter.Router.route()
    respond = (body, statusCode=200, headers) ->
      this.response.statusCode statusCode
      this.response.setHeader 'Content-Type', 'text/json'
      this.response.writeHead statusCode, headers
      this.response.write JSON.stringify(body)
      this.response.end()
    

    从客戶端這樣的东西:

    Meteor.startup ->
      HTTP.get "http://yoursite.com/pdf-server",
        headers:
          'X-Auth-Token': Accounts._storedLoginToken()
          'X-User-Id': Meteor.userId()
        (error, result) ->  // This callback triggered once http response received         
          console.log result
    

    此代碼在很大程度上受到RestStop和RestStop2的啟發.它是用於在Meteor 0.9.0+(基於Iron Router的頂部)中編寫REST API的meteor軟體包的一部分.您可以在此處查看完整的源代碼:

    https://github.com/krose72205/meteor-restivus

  • 11天前
    3 #

    由於服務器端路由充当簡單的REST端點,因此它们無法訪問使用者身份驗證資料(例如,他们可以 致電 Meteor.user() ).因此,您需要設計一種替代的身份驗證方案.最簡單的方法是使用此處和此處讨論的某種形式的密钥交換。

    示例實現:

    server/app.js

    // whenever the user logs in, update her apiKey
    Accounts.onLogin(function(info) {
      // generate a new apiKey
      var apiKey = Random.id();
      // add the apiKey to the user's document
      Meteor.users.update(info.user._id, {$set: {apiKey: apiKey}});
    });
    // auto-publish the current user's apiKey
    Meteor.publish(null, function() {
      return Meteor.users.find(this.userId, {fields: {apiKey: 1}});
    });
    

    lib/routes.js

    // example route using the apiKey
    Router.route('/secret/:apiKey', {name: 'secret', where: 'server'})
      .get(function() {
        // fetch the user with this key
        // note you may want to add an index on apiKey so this is fast
        var user = Meteor.users.findOne({apiKey: this.params.apiKey});
        if (user) {
          // we have authenticated the user - do something useful here
          this.response.statusCode = 200;
          return this.response.end('ok');
        } else {
          // the key is invalid or not provided so return an error
          this.response.statusCode = 403;
          return this.response.end('not allowed');
        }
      });
    

    client/app.html

    <template name="myTemplate">
        {{#with currentUser}}
          <a href="{{pathFor route='secret'}}">secret</a>
        {{/with}}
    </template>
    
    註釋

      Make /secret 只能通過HTTPS訪問。

      使用者請求 /secret的可能性很高 当前已連線,無法保證她已連線.使用者本可以登錄,然後複製她的密钥,然後關闭標簽頁並在以後的某个時間發起請求。

      這是一種簡單的使用者身份驗證方法.如果服務器路由顯示高value資料(SSN,信用卡等),我將探索更複雜的機製(請參见上面的鏈接)。

      有關从服務器發送靜態內容的更多详细資訊,請參见此問题。

      p

      我真的相信使用HTTP標頭是解決此問题的最佳方法,因為它们很簡單,不需要弄亂cookie或開發新的身份驗證方案。

      我類似@kahmali的答案,所以我寫了它来与webApp和簡單的XMLHttpRequest一起使用.已在Meteor 1.6上进行了測試。

      Client

      import { Meteor } from 'meteor/meteor';
      import { Accounts } from 'meteor/accounts-base';
      // Skipping ahead to the upload logic
      const xhr = new XMLHttpRequest();
      const form = new FormData();
      // Add files
      files.forEach((file) => {
        form.append(file.name,
          // So BusBoy sees as file instead of field, use Blob
          new Blob([file.data], { type: 'text/plain' })); // w/e your mime type is
      });
      // XHR progress, load, error, and readystatechange event listeners here
      // Open Connection
      xhr.open('POST', '/path/to/upload', true);
      // Meteor authentication details (must happen *after* xhr.open)
      xhr.setRequestHeader('X-Auth-Token', Accounts._storedLoginToken());
      xhr.setRequestHeader('X-User-Id', Meteor.userId());
      // Send
      xhr.send(form);
      

      Server

      import { Meteor } from 'meteor/meteor';
      import { WebApp } from 'meteor/webapp';
      import { Roles } from 'meteor/alanning:roles'; // optional
      const BusBoy = require('connect-busboy');
      const crypto = require('crypto'); // built-in Node library
      WebApp.connectHandlers
        .use(BusBoy())
        .use('/path/to/upload', (req, res) => {
          const user = req.headers['x-user-id'];
          // We have to get a base64 digest of the sha256 hashed login token
          // I'm not sure when Meteor changed to hashed tokens, but this is
          // one of the major differences from @kahmali's answer
          const hash = crypto.createHash('sha256');
          hash.update(req.headers['x-auth-token']);
          // Authentication (is user logged-in)
          if (!Meteor.users.findOne({
            _id: user,
            'services.resume.loginTokens.hashedToken': hash.digest('base64'),
          })) {
            // User not logged in; 401 Unauthorized
            res.writeHead(401);
            res.end();
            return;
          }
          // Authorization
          if (!Roles.userIsInRole(user, 'whatever')) {
            // User is not authorized; 403 Forbidden
            res.writeHead(403);
            res.end();
            return;
          }
          if (req.busboy) {
            // Handle file upload
            res.writeHead(201); // eventually
            res.end();
          } else {
            // Something went wrong
            res.writeHead(500); // server error
            res.end();
          }
        });
      

      我希望這對某人有帮助!

  • 11天前
    4 #

    由於Meteor不使用会话cookie,因此客戶端向服務器路由發出HTTP請求時,客戶端必须顯式包括某種使用者標識。

    最簡單的方法是在URL的查詢字元串中傳遞userId.顯然,您還需要添加一个安全令牌,以證明使用者確實是他们的主张.可以通過Meteor方法获取此令牌。

    流星本身不提供這種機製,因此您需要一些自定義實現.我寫了一个叫做 mhagmajer:server-route的流星包 经過全面測試.您可以在此處了解更多資訊:https://blog.hagmajer.com/server-side-routing-with-authentication-in-meteor-6625ed832a94

  • 如何在R指令碼中遍歷檔案名?
  • c++:在同一系統上的两个應用程式之間傳遞資料的首選方式是什麼?