从浏览器请求新页面时如何在 HTTP 标头中包含访问令牌 [英] How to include access-token in the HTTP header when requesting a new page from browser

查看:32
本文介绍了从浏览器请求新页面时如何在 HTTP 标头中包含访问令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

其他人提出了类似的问题(here) 但没有得到正确的答案.由于这对我来说是基本且重要的(也许对其他人也是如此),我想在这里问一下.我在服务器端使用 Node.js+Express+EJS.我努力通过在服务器上使用 jsonwebtoken 和在 Web 浏览器上使用 jQuery 的 ajax-jsonp 来使令牌身份验证成功.现在,在授予令牌并将其存储在浏览器端的 sessionStorage 中后,我可以使用请求标头中包含的令牌发起另一个 ajax 请求,以获取用户的个人资料并将其显示在当前"页面的某处.但我想要的是显示一个新网页来显示用户的个人资料,而不是在当前"页面(网站的主/索引页面)中显示它.问题是:

The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:

  1. 如何发起这样的HTTP GET请求,包括HTTP头中的token;并将响应显示为新网页?
  2. Node.js 如何处理这个问题?如果我使用 res.render 那么在哪里放置 js 逻辑来验证令牌和访问数据库并生成页面内容?

或者,我们应该说令牌机制更适合 API 身份验证而不是普通网页身份验证(Web 浏览器提供有限的 API)?

Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?

如果我们想使用令牌机制作为通用身份验证,我认为这个问题的答案很重要,因为在网站场景中,内容大多组织为服务器上的网页,而客户端的 API 由浏览器.

I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.

纯粹猜测,可能有另一种方式,ajax成功回调从当前页面创建一个新页面,并使用服务器的响应,但我也不知道如何实现这一点.

By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.

通过调用下面的代码成功返回了customer_profile.ejs中的HTML内容,但是客户端ajax(显然)拒绝了它.

By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.

exports.customer_profile = function (req, res) {
  var token = req.headers.token;
  var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
  var decoded = jwt.verify(token, public_key);
  var sql = 'SELECT * FROM  customer WHERE username = "' + decoded.sub + '"';
  util.conn.query(sql, function (err, rows) {
    if (!err) {
      for (var i = 0; i < rows.length; i++) {
        res.render('customer_profile', {customer_profile: rows[i]});
        break;
      }
    }
  });
};

推荐答案

我也在努力寻找解决方案.请注意,我将 Firebase 用于某些功能,但我会尽量记录逻辑.

I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.

到目前为止,我能够弄清楚的是:

So far what I was able to figure out is the following:

  1. 将自定义标头附加到 HTTP 请求客户端

// landing.js - main page script snippet
function loadPage(path) {
    // Get current user's ID Token
    firebase.auth().currentUser.getIdToken()
    .then(token => {
        // Make a fetch request to 'path'
        return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
            method: 'GET',
            headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
        });
    })
    .then(response => { 
        // As noted below, this part I haven't solved yet. 
        // TODO: Open response as new webpage instead of displaying as data in existing one
        return response.text();
    })
    .then(text => {
        console.log(text);
    })
    .catch(error => {
        console.log(error);
    });
}

  1. 根据您的逻辑通过检索相应的标头值服务器端来验证令牌

// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup). 
// getLocale - language negotiation. 
// getContext - auth token verification if it is available and appends it to Request object for convenience

app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);

// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"

// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
        const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
        if(!idToken) {
            return next(); // Passes to next middleware if no token, terminates further execution
        }
        admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
        .then(token => {
            req.decoded_token = token; // Append token to Request object for convenience in further middleware
            return next(); // Pass on further
        })
        .catch(error => {
            console.log('Request not authorized', 401, error)
            return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
        });
    }

  1. 渲染并发回数据

// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user

router.get('/console', middleware.getTranslation('console'), (req, res) => {
    if(req.decoded_token) { // if token was verified successfully and is appended to req
        res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
    } else {
        res.status(401).send('Not authorized'); // else send 401 to user
    }
});

如您所见,我能够模块化代码并使用自定义中间件使其整洁清晰.它现在是一个有效的 API,使用身份验证和受限访问从服务器返回数据

As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access

我还没有解决的:

如上所述,该解决方案使用 fetch API 并且请求的结果是来自服务器 (html) 的数据,而不是新页面(即跟踪锚链接时).这意味着现在使用此代码的唯一方法是使用 DOM 操作并将响应设置为页面的 innerHTML.MDN 建议您可以设置位置"标题,它会在浏览器中显示一个新的 URL(您希望指示的 URL).这意味着你实际上实现了你和我想要的,但如果你知道我的意思,我仍然无法理解如何像浏览器那样在你点击链接时显示它.

As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.

无论如何,请让我知道您对此有何看法,以及您是否能够从我尚未解决的部分中解决此问题

Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet

这篇关于从浏览器请求新页面时如何在 HTTP 标头中包含访问令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆